diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8685ac6cb..5674d615a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -180,6 +180,10 @@ dependencies { compileOnly(libs.lombok) annotationProcessor(libs.lombok) implementation(libs.markwon.core) + + implementation("com.github.bumptech.glide:glide:4.16.0") + implementation("com.google.code.gson:gson:2.11.0") + implementation("com.facebook.shimmer:shimmer:0.5.0") } configurations.all { diff --git a/app/src/main/java/com/wmods/wppenhacer/activities/AboutActivity.java b/app/src/main/java/com/wmods/wppenhacer/activities/AboutActivity.java index 9221ac240..a67b74076 100644 --- a/app/src/main/java/com/wmods/wppenhacer/activities/AboutActivity.java +++ b/app/src/main/java/com/wmods/wppenhacer/activities/AboutActivity.java @@ -1,76 +1,208 @@ package com.wmods.wppenhacer.activities; import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; -import android.view.ContextThemeWrapper; -import android.widget.LinearLayout; +import android.os.Handler; +import android.os.Looper; +import android.util.LruCache; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; -import com.google.android.material.button.MaterialButton; import com.wmods.wppenhacer.R; import com.wmods.wppenhacer.activities.base.BaseActivity; import com.wmods.wppenhacer.databinding.ActivityAboutBinding; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + public class AboutActivity extends BaseActivity { - private static final String[][] CONTRIBUTORS = { - {"Dev4Mod", "https://github.com/Dev4Mod"}, - {"frknkrc44", "https://github.com/frknkrc44"}, - {"mubashardev", "https://github.com/mubashardev"}, - {"masbentoooredoo", "https://github.com/masbentoooredoo"}, - {"zhongerxll", "https://github.com/zhongerxll"}, - {"BryanGIG", "https://github.com/BryanGIG"}, - {"rizqi-developer", "https://github.com/rizqi-developer"}, - {"pedroborraz", "https://github.com/pedroborraz"}, - {"ahmedtohamy1", "https://github.com/ahmedtohamy1"}, - {"mohdafix", "https://github.com/mohdafix"}, - {"maulana-kurniawan", "https://github.com/maulana-kurniawan"}, - {"erzachn", "https://github.com/erzachn"}, - {"cvnertnc", "https://github.com/cvnertnc"}, - {"rkorossy", "https://github.com/rkorossy"}, - {"StupidRepo", "https://github.com/StupidRepo"}, - {"Blank517", "https://github.com/Blank517"}, - {"astola-studio", "https://github.com/astola-studio"}, - {"Strange-IPmart", "https://github.com/Strange-IPmart"} - }; + private ActivityAboutBinding binding; + private ContributorAdapter adapter; + private List contributorList = new ArrayList<>(); + private static final String API_URL = "https://api.github.com/repos/mubashardev/WaEnhancer/contributors"; + private static final OkHttpClient client = new OkHttpClient(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - ActivityAboutBinding binding = ActivityAboutBinding.inflate(getLayoutInflater()); + binding = ActivityAboutBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - binding.btnTelegram.setOnClickListener(v -> openUrl("https://t.me/waenhancer")); - binding.btnGithub.setOnClickListener(view -> openUrl("https://github.com/Dev4Mod/WaEnhancer")); - - int topMargin = getResources().getDimensionPixelSize(R.dimen.spacing_small); - for (int i = 0; i < CONTRIBUTORS.length; i++) { - String[] contributor = CONTRIBUTORS[i]; - MaterialButton button = new MaterialButton(new ContextThemeWrapper(this, R.style.ModernButton_Outlined)); - button.setText(contributor[0]); - button.setIconResource(R.drawable.ic_github); - button.setIconGravity(MaterialButton.ICON_GRAVITY_TEXT_START); - button.setIconPadding(getResources().getDimensionPixelSize(R.dimen.spacing_small)); - LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ); - if (i > 0) { - params.topMargin = topMargin; - } - button.setLayoutParams(params); - button.setOnClickListener(v -> openUrl(contributor[1])); - binding.contributorsContainer.addView(button); + binding.btnTelegram.setOnClickListener(v -> openUrl("https://t.me/waenhancer1")); + binding.btnGithub.setOnClickListener(view -> openUrl("https://github.com/mubashardev/WaEnhancer")); + + adapter = new ContributorAdapter(); + binding.rvContributors.setAdapter(adapter); + + fetchContributors(); + } + + private void fetchContributors() { + android.content.SharedPreferences prefs = getSharedPreferences("github_api_cache", MODE_PRIVATE); + long lastFetch = prefs.getLong("last_fetch", 0); + String cachedJson = prefs.getString("contributors_json", null); + + // 1-hour cache + if (cachedJson != null && (System.currentTimeMillis() - lastFetch < 3600000)) { + parseContributorsAndRefresh(cachedJson); + return; } + Request request = new Request.Builder() + .url(API_URL) + .header("User-Agent", "WaEnhancer-App") + .header("Accept", "application/vnd.github.v3+json") + .build(); + + client.newCall(request).enqueue(new Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull java.io.IOException e) { + runOnUiThread(() -> { + // Fallback to cache if failed + if (cachedJson != null) { + parseContributorsAndRefresh(cachedJson); + } else { + Toast.makeText(AboutActivity.this, "Failed to load contributors", Toast.LENGTH_SHORT).show(); + } + }); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) throws java.io.IOException { + if (!response.isSuccessful() || response.body() == null) { + runOnUiThread(() -> { + if (cachedJson != null) { + parseContributorsAndRefresh(cachedJson); + } else { + Toast.makeText(AboutActivity.this, "Error fetching contributors", Toast.LENGTH_SHORT) + .show(); + } + }); + return; + } + + try { + String json = response.body().string(); + prefs.edit() + .putLong("last_fetch", System.currentTimeMillis()) + .putString("contributors_json", json) + .apply(); + + runOnUiThread(() -> parseContributorsAndRefresh(json)); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + + private void parseContributorsAndRefresh(String json) { + try { + JSONArray jsonArray = new JSONArray(json); + List fetchList = new ArrayList<>(); + + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject obj = jsonArray.getJSONObject(i); + Contributor c = new Contributor(); + c.login = obj.optString("login"); + c.avatarUrl = obj.optString("avatar_url"); + c.htmlUrl = obj.optString("html_url"); + c.contributions = obj.optInt("contributions", 0); + fetchList.add(c); + } + + contributorList.clear(); + contributorList.addAll(fetchList); + adapter.notifyDataSetChanged(); + + } catch (Exception e) { + e.printStackTrace(); + } } private void openUrl(String url) { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - startActivity(intent); + try { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + startActivity(intent); + } catch (Exception e) { + e.printStackTrace(); + } + } + private static class Contributor { + String login; + String avatarUrl; + String htmlUrl; + int contributions; } + + private class ContributorAdapter extends RecyclerView.Adapter { + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_contributor, parent, false); + return new ViewHolder(v); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + Contributor c = contributorList.get(position); + + holder.ivAvatar.clearColorFilter(); // Remove default tint when loading real image + com.bumptech.glide.Glide.with(holder.itemView.getContext()) + .load(new com.bumptech.glide.load.model.GlideUrl(c.avatarUrl, + new com.bumptech.glide.load.model.LazyHeaders.Builder() + .addHeader("User-Agent", "WaEnhancer-App") + .build())) + .placeholder(R.drawable.ic_github) + .into(holder.ivAvatar); + + holder.ivAvatar.setOnClickListener(v -> com.wmods.wppenhacer.ui.helpers.BottomSheetHelper + .showUserProfile(AboutActivity.this, c.login, c.avatarUrl, c.htmlUrl, c.contributions)); + } + + @Override + public int getItemCount() { + return contributorList.size(); + } + + class ViewHolder extends RecyclerView.ViewHolder { + ImageView ivAvatar; + + ViewHolder(View itemView) { + super(itemView); + ivAvatar = itemView.findViewById(R.id.ivAvatar); + } + } + } + } diff --git a/app/src/main/java/com/wmods/wppenhacer/ui/helpers/BottomSheetHelper.java b/app/src/main/java/com/wmods/wppenhacer/ui/helpers/BottomSheetHelper.java new file mode 100644 index 000000000..7b7058e77 --- /dev/null +++ b/app/src/main/java/com/wmods/wppenhacer/ui/helpers/BottomSheetHelper.java @@ -0,0 +1,436 @@ +package com.wmods.wppenhacer.ui.helpers; + +import android.content.Context; +import android.content.DialogInterface; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; + +import com.google.android.material.bottomsheet.BottomSheetDialog; +import com.google.android.material.button.MaterialButton; +import com.google.android.material.checkbox.MaterialCheckBox; +import com.google.android.material.radiobutton.MaterialRadioButton; +import com.google.android.material.textfield.TextInputEditText; +import com.google.android.material.textfield.TextInputLayout; +import com.google.android.material.textview.MaterialTextView; +import com.wmods.wppenhacer.R; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Global helper for showing professional bottom sheets throughout the app. + * Replaces AlertDialogs with consistent EU-aesthetic bottom sheets. + */ +public class BottomSheetHelper { + + public interface OnConfirmListener { + void onConfirm(); + } + + public interface OnInputConfirmListener { + void onConfirm(String input); + } + + // Extracted only the contributor profile methods. + + /** + * Create a styled BottomSheetDialog with transparent background. + */ + private static BottomSheetDialog createDialog(Context context) { + BottomSheetDialog dialog = new BottomSheetDialog(context); + dialog.setOnShowListener(d -> { + BottomSheetDialog bsd = (BottomSheetDialog) d; + View bottomSheet = bsd.findViewById(com.google.android.material.R.id.design_bottom_sheet); + if (bottomSheet != null) { + bottomSheet.setBackgroundResource(android.R.color.transparent); + } + }); + return dialog; + } + + private static final okhttp3.OkHttpClient client = new okhttp3.OkHttpClient(); + + public static void showUserProfile( + Context context, + String username, + String avatarUrl, + String htmlUrl, + int contributions) { + + BottomSheetDialog bottomSheet = createDialog(context); + View view = LayoutInflater.from(context).inflate(R.layout.bottom_sheet_user_profile, null); + bottomSheet.setContentView(view); + + com.google.android.material.imageview.ShapeableImageView ivAvatar = view.findViewById(R.id.bsAvatar); + com.google.android.material.textview.MaterialTextView tvName = view.findViewById(R.id.bsName); + com.google.android.material.textview.MaterialTextView tvUsername = view.findViewById(R.id.bsUsername); + + com.facebook.shimmer.ShimmerFrameLayout shimmerLayout = view.findViewById(R.id.bsShimmerLayout); + View contentLayout = view.findViewById(R.id.bsContentLayout); + + com.bumptech.glide.Glide.with(context) + .load(new com.bumptech.glide.load.model.GlideUrl(avatarUrl, + new com.bumptech.glide.load.model.LazyHeaders.Builder() + .addHeader("User-Agent", "WaEnhancer-App") + .build())) + .placeholder(R.drawable.ic_github) + .into(ivAvatar); + + tvName.setText(username); + tvUsername.setText("@" + username); + + shimmerLayout.setVisibility(View.VISIBLE); + shimmerLayout.startShimmer(); + contentLayout.setVisibility(View.GONE); + + bottomSheet.show(); + + android.content.SharedPreferences prefs = context.getSharedPreferences("github_user_cache", + Context.MODE_PRIVATE); + long lastFetch = prefs.getLong(username + "_time", 0); + String cachedJson = prefs.getString(username + "_json", null); + + if (cachedJson != null && (System.currentTimeMillis() - lastFetch < 3600000)) { + parseAndPopulateProfile(context, view, bottomSheet, cachedJson, htmlUrl, avatarUrl, contributions); + return; + } + + okhttp3.Request request = new okhttp3.Request.Builder() + .url("https://api.github.com/users/" + username) + .header("User-Agent", "WaEnhancer-App") + .header("Accept", "application/vnd.github.v3+json") + .build(); + + client.newCall(request).enqueue(new okhttp3.Callback() { + @Override + public void onFailure(@androidx.annotation.NonNull okhttp3.Call call, + @androidx.annotation.NonNull java.io.IOException e) { + new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> { + if (cachedJson != null) { + parseAndPopulateProfile(context, view, bottomSheet, cachedJson, htmlUrl, avatarUrl, + contributions); + } else { + android.widget.Toast + .makeText(context, "Failed to load user profile", android.widget.Toast.LENGTH_SHORT) + .show(); + bottomSheet.dismiss(); + } + }); + } + + @Override + public void onResponse(@androidx.annotation.NonNull okhttp3.Call call, + @androidx.annotation.NonNull okhttp3.Response response) throws java.io.IOException { + if (!response.isSuccessful() || response.body() == null) { + new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> { + if (cachedJson != null) { + parseAndPopulateProfile(context, view, bottomSheet, cachedJson, htmlUrl, avatarUrl, + contributions); + } else { + android.widget.Toast + .makeText(context, "Error fetching user", android.widget.Toast.LENGTH_SHORT).show(); + bottomSheet.dismiss(); + } + }); + return; + } + + try { + String json = response.body().string(); + prefs.edit() + .putLong(username + "_time", System.currentTimeMillis()) + .putString(username + "_json", json) + .apply(); + + new android.os.Handler(android.os.Looper.getMainLooper()) + .post(() -> parseAndPopulateProfile(context, view, bottomSheet, json, htmlUrl, avatarUrl, + contributions)); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + + private static void parseAndPopulateProfile(Context context, View view, BottomSheetDialog bottomSheet, String json, + String fallbackHtmlUrl, String avatarUrl, int contributions) { + try { + org.json.JSONObject obj = new org.json.JSONObject(json); + String name = obj.optString("name", ""); + String login = obj.optString("login", ""); + String location = obj.optString("location", ""); + String bio = obj.optString("bio", ""); + boolean hireable = obj.optBoolean("hireable", false); + int followers = obj.optInt("followers", 0); + String twitter = obj.optString("twitter_username", ""); + String blog = obj.optString("blog", ""); + String htmlUrl = obj.optString("html_url", fallbackHtmlUrl); + + if (name.isEmpty() || name.equals("null")) + name = login; + + com.google.android.material.textview.MaterialTextView tvName = view.findViewById(R.id.bsName); + com.google.android.material.textview.MaterialTextView tvLocation = view.findViewById(R.id.bsLocation); + com.google.android.material.textview.MaterialTextView tvFollowers = view.findViewById(R.id.bsFollowers); + com.google.android.material.textview.MaterialTextView tvBio = view.findViewById(R.id.bsBio); + com.google.android.material.textview.MaterialTextView tvContributions = view + .findViewById(R.id.bsContributions); + + android.widget.ImageView ivLocationIcon = view.findViewById(R.id.bsLocationIcon); + View locationContainer = view.findViewById(R.id.bsLocationContainer); + View chipsScroll = view.findViewById(R.id.bsChipsScroll); + + com.google.android.material.chip.Chip chipHireable = view.findViewById(R.id.chipHireable); + com.google.android.material.chip.Chip chipTwitter = view.findViewById(R.id.chipTwitter); + + com.google.android.material.button.MaterialButton btnGithub = view.findViewById(R.id.bsGithubBtn); + com.google.android.material.button.MaterialButton btnWebsite = view.findViewById(R.id.bsWebsiteBtn); + com.google.android.material.button.MaterialButton btnContributions = view + .findViewById(R.id.bsContributionsBtn); + + tvName.setText(name); + + boolean hasLocation = location != null && !location.isEmpty() && !location.equals("null"); + if (hasLocation || followers > 0 || contributions > 0) { + locationContainer.setVisibility(View.VISIBLE); + if (hasLocation) { + ivLocationIcon.setVisibility(View.VISIBLE); + tvLocation.setVisibility(View.VISIBLE); + tvLocation.setText(location); + } else { + ivLocationIcon.setVisibility(View.GONE); + tvLocation.setVisibility(View.GONE); + } + if (followers > 0) { + tvFollowers.setVisibility(View.VISIBLE); + tvFollowers.setText(hasLocation ? "• " + followers + " followers" : followers + " followers"); + } else { + tvFollowers.setVisibility(View.GONE); + } + if (contributions > 0) { + tvContributions.setVisibility(View.VISIBLE); + boolean hasPrev = hasLocation || followers > 0; + tvContributions.setText(hasPrev ? "• " + contributions + " commits" : contributions + " commits"); + + btnContributions.setVisibility(View.VISIBLE); + btnContributions.setVisibility(View.VISIBLE); + final String finalName = name; + btnContributions.setOnClickListener(v -> { + bottomSheet.dismiss(); + showContributions(context, login, finalName, avatarUrl, htmlUrl, contributions); + }); + } else { + tvContributions.setVisibility(View.GONE); + btnContributions.setVisibility(View.GONE); + } + } + + if (bio != null && !bio.isEmpty() && !bio.equals("null")) { + tvBio.setVisibility(View.VISIBLE); + tvBio.setText(bio); + } + + boolean hasChips = false; + if (hireable) { + hasChips = true; + chipHireable.setVisibility(View.VISIBLE); + chipHireable.setText("Open to hire"); + } + + if (twitter != null && !twitter.isEmpty() && !twitter.equals("null")) { + hasChips = true; + chipTwitter.setVisibility(View.VISIBLE); + chipTwitter.setText("@" + twitter); + chipTwitter.setOnClickListener(v -> { + try { + context.startActivity(new android.content.Intent(android.content.Intent.ACTION_VIEW, + android.net.Uri.parse("https://x.com/" + twitter))); + } catch (Exception ignored) { + } + }); + } + + if (blog != null && !blog.isEmpty() && !blog.equals("null")) { + btnWebsite.setVisibility(View.VISIBLE); + btnWebsite.setOnClickListener(v -> { + String url = blog; + if (!url.startsWith("http")) + url = "https://" + url; + try { + context.startActivity(new android.content.Intent(android.content.Intent.ACTION_VIEW, + android.net.Uri.parse(url))); + } catch (Exception ignored) { + } + }); + } + + if (hasChips) { + chipsScroll.setVisibility(View.VISIBLE); + } + + btnGithub.setOnClickListener(v -> { + try { + context.startActivity(new android.content.Intent(android.content.Intent.ACTION_VIEW, + android.net.Uri.parse(htmlUrl))); + bottomSheet.dismiss(); + } catch (Exception ignored) { + } + }); + + com.facebook.shimmer.ShimmerFrameLayout shimmerLayout = view.findViewById(R.id.bsShimmerLayout); + View contentLayout = view.findViewById(R.id.bsContentLayout); + + shimmerLayout.stopShimmer(); + shimmerLayout.setVisibility(View.GONE); + contentLayout.setVisibility(View.VISIBLE); + + } catch (Exception e) { + e.printStackTrace(); + android.widget.Toast.makeText(context, "Failed to parse profile", android.widget.Toast.LENGTH_SHORT).show(); + bottomSheet.dismiss(); + } + } + + private static void showContributions(Context context, String login, String displayName, String avatarUrl, + String htmlUrl, int contributions) { + BottomSheetDialog bottomSheet = createDialog(context); + View view = LayoutInflater.from(context).inflate(R.layout.bottom_sheet_contributions, null); + bottomSheet.setContentView(view); + + android.widget.ImageButton btnBack = view.findViewById(R.id.bsContribBackBtn); + btnBack.setOnClickListener(v -> { + bottomSheet.dismiss(); + showUserProfile(context, login, avatarUrl, htmlUrl, contributions); + }); + + com.google.android.material.textview.MaterialTextView tvTitle = view.findViewById(R.id.bsContribTitle); + tvTitle.setText(displayName + "'s Contributions"); + + com.facebook.shimmer.ShimmerFrameLayout shimmerLayout = view.findViewById(R.id.bsContribShimmer); + View contentLayout = view.findViewById(R.id.bsContribContent); + + shimmerLayout.setVisibility(View.VISIBLE); + shimmerLayout.startShimmer(); + contentLayout.setVisibility(View.GONE); + + bottomSheet.show(); + + android.content.SharedPreferences prefs = context.getSharedPreferences("github_user_cache", + Context.MODE_PRIVATE); + long lastFetch = prefs.getLong("repo_stats_time", 0); + String cachedJson = prefs.getString("repo_stats_json", null); + + if (cachedJson != null && (System.currentTimeMillis() - lastFetch < 3600000)) { + parseAndPopulateContributions(context, view, bottomSheet, cachedJson, login); + return; + } + + okhttp3.Request request = new okhttp3.Request.Builder() + .url("https://api.github.com/repos/mubashardev/WaEnhancer/stats/contributors") + .header("User-Agent", "WaEnhancer-App") + .header("Accept", "application/vnd.github.v3+json") + .build(); + + client.newCall(request).enqueue(new okhttp3.Callback() { + @Override + public void onFailure(@androidx.annotation.NonNull okhttp3.Call call, + @androidx.annotation.NonNull java.io.IOException e) { + new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> { + if (cachedJson != null) { + parseAndPopulateContributions(context, view, bottomSheet, cachedJson, login); + } else { + android.widget.Toast + .makeText(context, "Failed to load contributions", android.widget.Toast.LENGTH_SHORT) + .show(); + bottomSheet.dismiss(); + } + }); + } + + @Override + public void onResponse(@androidx.annotation.NonNull okhttp3.Call call, + @androidx.annotation.NonNull okhttp3.Response response) throws java.io.IOException { + + // GitHub stats API can return 202 Accepted if calculating + if (!response.isSuccessful() || response.body() == null || response.code() == 202) { + new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> { + if (cachedJson != null) { + parseAndPopulateContributions(context, view, bottomSheet, cachedJson, login); + } else { + android.widget.Toast.makeText(context, "GitHub is calculating stats, try again later.", + android.widget.Toast.LENGTH_SHORT).show(); + bottomSheet.dismiss(); + } + }); + return; + } + + try { + String json = response.body().string(); + prefs.edit() + .putLong("repo_stats_time", System.currentTimeMillis()) + .putString("repo_stats_json", json) + .apply(); + + new android.os.Handler(android.os.Looper.getMainLooper()) + .post(() -> parseAndPopulateContributions(context, view, bottomSheet, json, login)); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + + private static void parseAndPopulateContributions(Context context, View view, BottomSheetDialog bottomSheet, + String json, String login) { + try { + org.json.JSONArray jsonArray = new org.json.JSONArray(json); + int totalCommits = 0; + long linesAdded = 0; + long linesDeleted = 0; + + for (int i = 0; i < jsonArray.length(); i++) { + org.json.JSONObject obj = jsonArray.getJSONObject(i); + org.json.JSONObject author = obj.optJSONObject("author"); + if (author != null) { + String authorLogin = author.optString("login"); + if (authorLogin.equalsIgnoreCase(login)) { + totalCommits = obj.optInt("total", 0); + org.json.JSONArray weeks = obj.optJSONArray("weeks"); + if (weeks != null) { + for (int j = 0; j < weeks.length(); j++) { + org.json.JSONObject week = weeks.getJSONObject(j); + linesAdded += week.optLong("a", 0); + linesDeleted += week.optLong("d", 0); + } + } + break; + } + } + } + + com.google.android.material.textview.MaterialTextView tvTotal = view.findViewById(R.id.bsContribTotal); + com.google.android.material.textview.MaterialTextView tvAdded = view.findViewById(R.id.bsContribAdded); + com.google.android.material.textview.MaterialTextView tvDeleted = view.findViewById(R.id.bsContribDeleted); + com.facebook.shimmer.ShimmerFrameLayout shimmerLayout = view.findViewById(R.id.bsContribShimmer); + View contentLayout = view.findViewById(R.id.bsContribContent); + + java.text.NumberFormat format = java.text.NumberFormat.getInstance(); + tvTotal.setText(format.format(totalCommits)); + tvAdded.setText("+ " + format.format(linesAdded)); + tvDeleted.setText("~ " + format.format(linesDeleted)); + + shimmerLayout.stopShimmer(); + shimmerLayout.setVisibility(View.GONE); + contentLayout.setVisibility(View.VISIBLE); + } catch (Exception e) { + e.printStackTrace(); + android.widget.Toast.makeText(context, "Failed to parse contributions", android.widget.Toast.LENGTH_SHORT) + .show(); + bottomSheet.dismiss(); + } + } +} diff --git a/app/src/main/java/com/wmods/wppenhacer/utils/FeatureCatalog.java b/app/src/main/java/com/wmods/wppenhacer/utils/FeatureCatalog.java index a7d09b6bb..a3fba8176 100644 --- a/app/src/main/java/com/wmods/wppenhacer/utils/FeatureCatalog.java +++ b/app/src/main/java/com/wmods/wppenhacer/utils/FeatureCatalog.java @@ -17,999 +17,1007 @@ */ public class FeatureCatalog { - private static List features; - - /** - * Initialize and return the complete feature catalog. - */ - public static List getAllFeatures(Context context) { - if (features == null) { - features = buildFeatureCatalog(context); + private static List features; + + /** + * Initialize and return the complete feature catalog. + */ + public static List getAllFeatures(Context context) { + if (features == null) { + features = buildFeatureCatalog(context); + } + return features; } - return features; - } - - /** - * Search features by query string. - * Returns all features that match the search query. - */ - public static List search(Context context, String query) { - if (query == null || query.trim().isEmpty()) { - return new ArrayList<>(); + + /** + * Search features by query string. + * Returns all features that match the search query. + */ + public static List search(Context context, String query) { + if (query == null || query.trim().isEmpty()) { + return new ArrayList<>(); + } + + return getAllFeatures(context).stream() + .filter(feature -> feature.matches(query)) + .collect(Collectors.toList()); + } + + /** + * Build the complete feature catalog from all preference XMLs. + */ + private static List buildFeatureCatalog(Context context) { + List catalog = new ArrayList<>(); + + // GENERAL FRAGMENT - General sub-preferences + catalog.add(new SearchableFeature("thememode", + context.getString(R.string.theme_mode), + context.getString(R.string.theme_mode_sum), + SearchableFeature.Category.GENERAL_HOME, + SearchableFeature.FragmentType.GENERAL, + "general_home", + Arrays.asList("dark", "light", "theme"))); + + catalog.add(new SearchableFeature("update_check", + context.getString(R.string.update_check), + context.getString(R.string.update_check_sum), + SearchableFeature.Category.GENERAL_HOME, + SearchableFeature.FragmentType.GENERAL, + "general_home", + Arrays.asList("update", "check", "automatic"))); + + catalog.add(new SearchableFeature("disable_expiration", + context.getString(R.string.disable_whatsapp_expiration), + context.getString(R.string.disable_whatsapp_expiration_sum), + SearchableFeature.Category.GENERAL_HOME, + SearchableFeature.FragmentType.GENERAL, + "general_home", + Arrays.asList("expiration", "version"))); + + catalog.add(new SearchableFeature("force_restore_backup_feature", + context.getString(R.string.force_restore_backup), + context.getString(R.string.force_restore_backup_summary), + SearchableFeature.Category.GENERAL_HOME, + SearchableFeature.FragmentType.GENERAL, + "general_home", + Arrays.asList("backup", "restore", "force"))); + + catalog.add(new SearchableFeature("disable_ads", + context.getString(R.string.disable_ads), + context.getString(R.string.disable_ads_sum), + SearchableFeature.Category.GENERAL_HOME, + SearchableFeature.FragmentType.GENERAL, + "general_home", + Arrays.asList("ads", "advertising", "block"))); + + catalog.add(new SearchableFeature("lite_mode", + context.getString(R.string.lite_mode), + context.getString(R.string.lite_mode_sum), + SearchableFeature.Category.GENERAL_HOME, + SearchableFeature.FragmentType.GENERAL, + "general_home", + Arrays.asList("lite", "performance", "battery"))); + + catalog.add(new SearchableFeature("force_english", + context.getString(R.string.force_english), + null, + SearchableFeature.Category.GENERAL_HOME, + SearchableFeature.FragmentType.GENERAL, + "general_home", + Arrays.asList("english", "language"))); + + catalog.add(new SearchableFeature("enablelogs", + context.getString(R.string.verbose_logs), + null, + SearchableFeature.Category.GENERAL_HOME, + SearchableFeature.FragmentType.GENERAL, + "general_home", + Arrays.asList("logs", "debug", "verbose"))); + + catalog.add(new SearchableFeature("bypass_version_check", + context.getString(R.string.disable_version_check), + context.getString(R.string.disable_version_check_sum), + SearchableFeature.Category.GENERAL_HOME, + SearchableFeature.FragmentType.GENERAL, + "general_home", + Arrays.asList("version", "check", "bypass"))); + + catalog.add(new SearchableFeature("bootloader_spoofer", + context.getString(R.string.bootloader_spoofer), + context.getString(R.string.bootloader_spoofer_sum), + SearchableFeature.Category.GENERAL_HOME, + SearchableFeature.FragmentType.GENERAL, + "general_home", + Arrays.asList("bootloader", "spoofer", "ban"))); + + catalog.add(new SearchableFeature("ampm", + context.getString(R.string.ampm), + null, + SearchableFeature.Category.GENERAL_HOME, + SearchableFeature.FragmentType.GENERAL, + "general_home", + Arrays.asList("time", "12", "hour", "format"))); + + catalog.add(new SearchableFeature("segundos", + context.getString(R.string.segundosnahora), + context.getString(R.string.segundosnahora_sum), + SearchableFeature.Category.GENERAL_HOME, + SearchableFeature.FragmentType.GENERAL, + "general_home", + Arrays.asList("seconds", "timestamp", "time"))); + + catalog.add(new SearchableFeature("secondstotime", + context.getString(R.string.textonahora), + context.getString(R.string.textonahora_sum), + SearchableFeature.Category.GENERAL_HOME, + SearchableFeature.FragmentType.GENERAL, + "general_home", + Arrays.asList("text", "timestamp", "custom"))); + + catalog.add(new SearchableFeature("tasker", + context.getString(R.string.enable_tasker_automation), + context.getString(R.string.enable_tasker_automation_sum), + SearchableFeature.Category.GENERAL_HOME, + SearchableFeature.FragmentType.GENERAL, + "general_home", + Arrays.asList("tasker", "automation", "intent"))); + + // GENERAL FRAGMENT - Homescreen sub-preferences + catalog.add(new SearchableFeature("buttonaction", + context.getString(R.string.show_menu_buttons_as_icons), + context.getString(R.string.show_menu_buttons_as_icons_sum), + SearchableFeature.Category.GENERAL_HOMESCREEN, + SearchableFeature.FragmentType.GENERAL, + "homescreen", + Arrays.asList("menu", "icons", "buttons"))); + + catalog.add(new SearchableFeature("shownamehome", + context.getString(R.string.showname), + context.getString(R.string.showname_sum), + SearchableFeature.Category.GENERAL_HOMESCREEN, + SearchableFeature.FragmentType.GENERAL, + "homescreen", + Arrays.asList("name", "profile", "title"))); + + catalog.add(new SearchableFeature("showbiohome", + context.getString(R.string.showbio), + context.getString(R.string.showbio_sum), + SearchableFeature.Category.GENERAL_HOMESCREEN, + SearchableFeature.FragmentType.GENERAL, + "homescreen", + Arrays.asList("bio", "status", "toolbar"))); + + catalog.add(new SearchableFeature("show_dndmode", + context.getString(R.string.show_dnd_button), + context.getString(R.string.show_dnd_button_sum), + SearchableFeature.Category.GENERAL_HOMESCREEN, + SearchableFeature.FragmentType.GENERAL, + "homescreen", + Arrays.asList("dnd", "do not disturb", "button"))); + + catalog.add(new SearchableFeature("newchat", + context.getString(R.string.enable_new_chat_button), + context.getString(R.string.enable_new_chat_button_sum), + SearchableFeature.Category.GENERAL_HOMESCREEN, + SearchableFeature.FragmentType.GENERAL, + "homescreen", + Arrays.asList("new", "chat", "button"))); + + catalog.add(new SearchableFeature("restartbutton", + context.getString(R.string.enable_restart_button), + context.getString(R.string.enable_restart_button_sum), + SearchableFeature.Category.GENERAL_HOMESCREEN, + SearchableFeature.FragmentType.GENERAL, + "homescreen", + Arrays.asList("restart", "reboot", "button"))); + + catalog.add(new SearchableFeature("open_wae", + context.getString(R.string.enable_wa_enhancer_button), + context.getString(R.string.enable_wa_enhancer_button_sum), + SearchableFeature.Category.GENERAL_HOMESCREEN, + SearchableFeature.FragmentType.GENERAL, + "homescreen", + Arrays.asList("wa enhancer", "open", "button"))); + + catalog.add(new SearchableFeature("separategroups", + context.getString(R.string.separate_groups), + context.getString(R.string.separate_groups_sum), + SearchableFeature.Category.GENERAL_HOMESCREEN, + SearchableFeature.FragmentType.GENERAL, + "homescreen", + Arrays.asList("separate", "groups", "filter"))); + + catalog.add(new SearchableFeature("filtergroups", + context.getString(R.string.new_ui_group_filter), + context.getString(R.string.new_ui_group_filter_sum), + SearchableFeature.Category.GENERAL_HOMESCREEN, + SearchableFeature.FragmentType.GENERAL, + "homescreen", + Arrays.asList("filter", "groups", "ui"))); + + catalog.add(new SearchableFeature("dotonline", + context.getString(R.string.show_online_dot_in_conversation_list), + context.getString(R.string.show_online_dot_in_conversation_list_sum), + SearchableFeature.Category.GENERAL_HOMESCREEN, + SearchableFeature.FragmentType.GENERAL, + "homescreen", + Arrays.asList("online", "dot", "green"))); + + catalog.add(new SearchableFeature("showonlinetext", + context.getString(R.string.show_online_last_seen_in_conversation_list), + context.getString(R.string.show_online_last_seen_in_conversation_list_sum), + SearchableFeature.Category.GENERAL_HOMESCREEN, + SearchableFeature.FragmentType.GENERAL, + "homescreen", + Arrays.asList("online", "last seen", "text"))); + + catalog.add(new SearchableFeature("filterseen", + context.getString(R.string.enable_filter_chats), + context.getString(R.string.enable_filter_chats_sum), + SearchableFeature.Category.GENERAL_HOMESCREEN, + SearchableFeature.FragmentType.GENERAL, + "homescreen", + Arrays.asList("filter", "chats", "unseen"))); + + catalog.add(new SearchableFeature("metaai", + context.getString(R.string.disable_metaai), + context.getString(R.string.disable_metaai_sum), + SearchableFeature.Category.GENERAL_HOMESCREEN, + SearchableFeature.FragmentType.GENERAL, + "homescreen", + Arrays.asList("meta", "ai", "disable"))); + + catalog.add(new SearchableFeature("chatfilter", + context.getString(R.string.novofiltro), + context.getString(R.string.novofiltro_sum), + SearchableFeature.Category.GENERAL_HOMESCREEN, + SearchableFeature.FragmentType.GENERAL, + "homescreen", + Arrays.asList("search", "filter", "icon", "bar"))); + + catalog.add(new SearchableFeature("disable_profile_status", + context.getString(R.string.disable_status_in_the_profile_photo), + context.getString(R.string.disable_status_in_the_profile_photo_sum), + SearchableFeature.Category.GENERAL_HOMESCREEN, + SearchableFeature.FragmentType.GENERAL, + "homescreen", + Arrays.asList("status", "profile", "photo", "circle"))); + + // GENERAL FRAGMENT - Conversation sub-preferences + catalog.add(new SearchableFeature("showonline", + context.getString(R.string.show_toast_on_contact_online), + context.getString(R.string.show_toast_on_contact_online_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("toast", "online", "notification"))); + + catalog.add(new SearchableFeature("toastdeleted", + context.getString(R.string.toast_on_delete), + context.getString(R.string.toast_on_delete_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("toast", "deleted", "notification"))); + + catalog.add(new SearchableFeature("toast_viewed_message", + context.getString(R.string.toast_on_viewed_message), + context.getString(R.string.toast_on_viewed_message_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("toast", "viewed", "read", "notification"))); + + catalog.add(new SearchableFeature("antirevoke", + context.getString(R.string.antirevoke), + context.getString(R.string.antirevoke_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("anti", "revoke", "delete", "deleted"))); + + catalog.add(new SearchableFeature("antirevokestatus", + context.getString(R.string.antirevokestatus), + context.getString(R.string.antirevokestatus_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("anti", "delete", "status"))); + + catalog.add(new SearchableFeature("antidisappearing", + context.getString(R.string.antidisappearing), + context.getString(R.string.antidisappearing_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("anti", "disappearing", "temporary", "messages"))); + + catalog.add(new SearchableFeature("broadcast_tag", + context.getString(R.string.show_chat_broadcast_icon), + context.getString(R.string.show_chat_broadcast_icon_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("broadcast", "icon", "tag"))); + + catalog.add(new SearchableFeature("pinnedlimit", + context.getString(R.string.disable_pinned_limit), + context.getString(R.string.disable_pinned_limit_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("pinned", "limit", "chats"))); + + catalog.add(new SearchableFeature("removeforwardlimit", + context.getString(R.string.removeforwardlimit), + context.getString(R.string.removeforwardlimit_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("forward", "limit", "remove"))); + + catalog.add(new SearchableFeature("hidetag", + context.getString(R.string.hidetag), + context.getString(R.string.hidetag_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("forwarded", "tag", "hide"))); + + catalog.add(new SearchableFeature("revokeallmessages", + context.getString(R.string.delete_for_everyone_all_messages), + context.getString(R.string.delete_for_everyone_all_messages_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("delete", "everyone", "limit", "revoke"))); + + catalog.add(new SearchableFeature("removeseemore", + context.getString(R.string.remove_see_more_button), + context.getString(R.string.remove_see_more_button_), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("see more", "button", "remove"))); + + catalog.add(new SearchableFeature("antieditmessages", + context.getString(R.string.show_edited_message_history), + context.getString(R.string.show_edited_message_history_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("edited", "history", "message"))); + + catalog.add(new SearchableFeature("alertsticker", + context.getString(R.string.enable_confirmation_to_send_sticker), + context.getString(R.string.enable_confirmation_to_send_sticker_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("sticker", "confirmation", "alert"))); + + catalog.add(new SearchableFeature("calltype", + context.getString(R.string.selection_of_call_type), + context.getString(R.string.selection_of_call_type_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("call", "type", "selection", "phone"))); + + catalog.add(new SearchableFeature("disable_defemojis", + context.getString(R.string.disable_default_emojis), + context.getString(R.string.disable_default_emojis_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("emoji", "default", "disable"))); + + catalog.add(new SearchableFeature("stamp_copied_message", + context.getString(R.string.stamp_copied_messages), + context.getString(R.string.stamp_copied_messages_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("copy", "stamp", "copied", "messages"))); + + catalog.add(new SearchableFeature("doubletap2like", + context.getString(R.string.double_click_to_react), + context.getString(R.string.double_click_to_like_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("double", "tap", "click", "react", "like"))); + + catalog.add(new SearchableFeature("doubletap2like_emoji", + context.getString(R.string.custom_reaction), + context.getString(R.string.custom_reaction_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("reaction", "emoji", "custom"))); + + catalog.add(new SearchableFeature("google_translate", + context.getString(R.string.google_translate), + context.getString(R.string.google_translate_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("translate", "google", "language"))); + + catalog.add(new SearchableFeature("deleted_messages_activity", + context.getString(R.string.deleted_messages_title), + context.getString(R.string.deleted_messages_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.ACTIVITY, + null, + Arrays.asList("deleted", "messages", "restore", "history", "log"))); + + catalog.add(new SearchableFeature("verify_blocked_contact", + context.getString(R.string.show_contact_blocked), + context.getString(R.string.show_contact_blocked_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("blocked", "contact", "verify"))); + + catalog.add(new SearchableFeature("video_note_attachment", + context.getString(R.string.video_note_attachment), + context.getString(R.string.video_note_attachment_sum), + SearchableFeature.Category.GENERAL_CONVERSATION, + SearchableFeature.FragmentType.GENERAL, + "conversation", + Arrays.asList("video", "note", "attachment", "ptv"))); + + // GENERAL FRAGMENT - Status + catalog.add(new SearchableFeature("autonext_status", + context.getString(R.string.disable_auto_status), + context.getString(R.string.disable_auto_status_sum), + SearchableFeature.Category.GENERAL, + SearchableFeature.FragmentType.GENERAL, + null, + Arrays.asList("auto", "status", "skip"))); + + catalog.add(new SearchableFeature("copystatus", + context.getString(R.string.enable_copy_status), + context.getString(R.string.enable_copy_status_sum), + SearchableFeature.Category.GENERAL, + SearchableFeature.FragmentType.GENERAL, + null, + Arrays.asList("copy", "status", "caption"))); + + catalog.add(new SearchableFeature("toast_viewed_status", + context.getString(R.string.toast_on_viewed_status), + context.getString(R.string.toast_on_viewed_status_sum), + SearchableFeature.Category.GENERAL, + SearchableFeature.FragmentType.GENERAL, + null, + Arrays.asList("toast", "viewed", "status", "notification"))); + + // PRIVACY FRAGMENT + catalog.add(new SearchableFeature("typearchive", + context.getString(R.string.hide_archived_chat), + context.getString(R.string.hide_archived_chat_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("archive", "hide", "hidden"))); + + catalog.add(new SearchableFeature("show_freezeLastSeen", + context.getString(R.string.show_freezeLastSeen_button), + context.getString(R.string.show_freezeLastSeen_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("freeze", "last seen", "button"))); + + catalog.add(new SearchableFeature("ghostmode", + context.getString(R.string.ghost_mode_title), + context.getString(R.string.ghost_mode_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("ghost", "mode", "invisible"))); + + catalog.add(new SearchableFeature("always_online", + context.getString(R.string.always_online), + context.getString(R.string.always_online_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("always", "online", "status"))); + + catalog.add(new SearchableFeature("lockedchats_enhancer", + context.getString(R.string.lockedchats_enhancer), + context.getString(R.string.lockedchats_enhancer_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("locked", "chats", "enhanced"))); + + catalog.add(new SearchableFeature("custom_privacy_type", + context.getString(R.string.custom_privacy_per_contact), + context.getString(R.string.custom_privacy_per_contact_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("custom", "privacy", "contact"))); + + catalog.add(new SearchableFeature("freezelastseen", + context.getString(R.string.freezelastseen), + context.getString(R.string.freezelastseen_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("freeze", "last seen"))); + + catalog.add(new SearchableFeature("hideread", + context.getString(R.string.hideread), + context.getString(R.string.hideread_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("hide", "read", "blue", "ticks"))); + + catalog.add(new SearchableFeature("hide_seen_view", + context.getString(R.string.view_seen_tick), + context.getString(R.string.view_seen_tick_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("view", "seen", "tick"))); + + catalog.add(new SearchableFeature("blueonreply", + context.getString(R.string.blueonreply), + context.getString(R.string.blueonreply_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("blue", "tick", "reply"))); + + catalog.add(new SearchableFeature("hideread_group", + context.getString(R.string.hideread_group), + context.getString(R.string.hideread_group_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("hide", "read", "group", "ticks"))); + + catalog.add(new SearchableFeature("hidereceipt", + context.getString(R.string.hidereceipt), + context.getString(R.string.hidereceipt_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("hide", "delivered", "receipt"))); + + catalog.add(new SearchableFeature("ghostmode_t", + context.getString(R.string.ghostmode), + context.getString(R.string.ghostmode_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("ghost", "typing", "hide"))); + + catalog.add(new SearchableFeature("ghostmode_r", + context.getString(R.string.ghostmode_r), + context.getString(R.string.ghostmode_sum_r), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("ghost", "recording", "audio"))); + + catalog.add(new SearchableFeature("hideonceseen", + context.getString(R.string.hide_once_view_seen), + context.getString(R.string.hide_once_view_seen_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("view", "once", "seen", "hide"))); + + catalog.add(new SearchableFeature("hideaudioseen", + context.getString(R.string.hide_audio_seen), + context.getString(R.string.hide_audio_seen_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("audio", "seen", "hide"))); + + catalog.add(new SearchableFeature("viewonce", + context.getString(R.string.viewonce), + context.getString(R.string.viewonce_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("view", "once", "unlimited"))); + + catalog.add(new SearchableFeature("seentick", + context.getString(R.string.show_button_to_send_blue_tick), + context.getString(R.string.show_button_to_send_blue_tick_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("blue", "tick", "button", "mark", "read"))); + + catalog.add(new SearchableFeature("hidestatusview", + context.getString(R.string.hidestatusview), + context.getString(R.string.hidestatusview_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("status", "view", "hide"))); + + catalog.add(new SearchableFeature("call_info", + context.getString(R.string.additional_call_information), + context.getString(R.string.additional_call_information_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("call", "information", "additional"))); + + catalog.add(new SearchableFeature("call_privacy", + context.getString(R.string.call_blocker), + context.getString(R.string.call_blocker_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("call", "blocker", "block"))); + + catalog.add(new SearchableFeature("call_type", + context.getString(R.string.call_blocking_type), + context.getString(R.string.call_blocking_type_sum), + SearchableFeature.Category.PRIVACY, + SearchableFeature.FragmentType.PRIVACY, + null, + Arrays.asList("call", "blocking", "type"))); + + // Continue in next part... + addMediaFeatures(context, catalog); + addCustomizationFeatures(context, catalog); + addHomeActions(context, catalog); + + return catalog; + } + + private static void addMediaFeatures(Context context, List catalog) { + catalog.add(new SearchableFeature("imagequality", + context.getString(R.string.imagequality), + context.getString(R.string.imagequality_sum), + SearchableFeature.Category.MEDIA, + SearchableFeature.FragmentType.MEDIA, + null, + Arrays.asList("image", "quality", "hd"))); + + catalog.add(new SearchableFeature("download_local", + context.getString(R.string.local_download), + null, + SearchableFeature.Category.MEDIA, + SearchableFeature.FragmentType.MEDIA, + null, + Arrays.asList("download", "local", "folder"))); + + catalog.add(new SearchableFeature("downloadstatus", + context.getString(R.string.statusdowload), + context.getString(R.string.statusdowload_sum), + SearchableFeature.Category.MEDIA, + SearchableFeature.FragmentType.MEDIA, + null, + Arrays.asList("download", "status", "share"))); + + catalog.add(new SearchableFeature("downloadviewonce", + context.getString(R.string.downloadviewonce), + context.getString(R.string.downloadviewonce_sum), + SearchableFeature.Category.MEDIA, + SearchableFeature.FragmentType.MEDIA, + null, + Arrays.asList("download", "view", "once"))); + + catalog.add(new SearchableFeature("video_limit_size", + context.getString(R.string.increase_video_size_limit), + null, + SearchableFeature.Category.MEDIA, + SearchableFeature.FragmentType.MEDIA, + null, + Arrays.asList("video", "size", "limit", "mb"))); + + catalog.add(new SearchableFeature("videoquality", + context.getString(R.string.videoquality), + context.getString(R.string.videoquality_sum), + SearchableFeature.Category.MEDIA, + SearchableFeature.FragmentType.MEDIA, + null, + Arrays.asList("video", "quality", "hd"))); + + catalog.add(new SearchableFeature("video_real_resolution", + context.getString(R.string.send_video_in_real_resolution), + context.getString(R.string.send_video_in_real_resolution_sum), + SearchableFeature.Category.MEDIA, + SearchableFeature.FragmentType.MEDIA, + null, + Arrays.asList("video", "resolution", "real"))); + + catalog.add(new SearchableFeature("video_maxfps", + context.getString(R.string.send_video_in_60fps), + context.getString(R.string.send_video_in_60fps_sum), + SearchableFeature.Category.MEDIA, + SearchableFeature.FragmentType.MEDIA, + null, + Arrays.asList("video", "60fps", "fps"))); + + catalog.add(new SearchableFeature("call_recording_enable", + context.getString(R.string.call_recording_enable), + context.getString(R.string.call_recording_enable_sum), + SearchableFeature.Category.MEDIA, + SearchableFeature.FragmentType.MEDIA, + null, + Arrays.asList("call", "recording", "record"))); + + catalog.add(new SearchableFeature("call_recording_path", + context.getString(R.string.call_recording_path), + null, + SearchableFeature.Category.MEDIA, + SearchableFeature.FragmentType.MEDIA, + null, + Arrays.asList("recording", "path", "folder"))); + + catalog.add(new SearchableFeature("call_recording_toast", + context.getString(R.string.call_recording_toast_title), + context.getString(R.string.call_recording_toast_summary), + SearchableFeature.Category.MEDIA, + SearchableFeature.FragmentType.MEDIA, + null, + Arrays.asList("recording", "toast", "notification", "show", "hide"))); + + catalog.add(new SearchableFeature("disable_sensor_proximity", + context.getString(R.string.disable_the_proximity_sensor), + context.getString(R.string.disable_the_proximity_sensor_sum), + SearchableFeature.Category.MEDIA, + SearchableFeature.FragmentType.MEDIA, + null, + Arrays.asList("proximity", "sensor", "screen"))); + + catalog.add(new SearchableFeature("proximity_audios", + context.getString(R.string.disable_audio_sensor), + context.getString(R.string.disable_audio_sensor_sum), + SearchableFeature.Category.MEDIA, + SearchableFeature.FragmentType.MEDIA, + null, + Arrays.asList("audio", "proximity", "sensor"))); + + catalog.add(new SearchableFeature("audio_type", + context.getString(R.string.send_audio_as_voice_audio_note), + context.getString(R.string.send_audio_as_voice_audio_note_sum), + SearchableFeature.Category.MEDIA, + SearchableFeature.FragmentType.MEDIA, + null, + Arrays.asList("audio", "voice", "note"))); + + catalog.add(new SearchableFeature("voicenote_speed", + context.getString(R.string.voice_note_speed), + null, + SearchableFeature.Category.MEDIA, + SearchableFeature.FragmentType.MEDIA, + null, + Arrays.asList("voice", "note", "speed"))); + + catalog.add(new SearchableFeature("audio_transcription", + context.getString(R.string.audio_transcription), + context.getString(R.string.audio_transcription_sum), + SearchableFeature.Category.MEDIA, + SearchableFeature.FragmentType.MEDIA, + null, + Arrays.asList("audio", "transcription", "text"))); + + catalog.add(new SearchableFeature("transcription_provider", + context.getString(R.string.transcription_provider), + context.getString(R.string.transcription_provider_sum), + SearchableFeature.Category.MEDIA, + SearchableFeature.FragmentType.MEDIA, + null, + Arrays.asList("transcription", "provider", "ai"))); + + catalog.add(new SearchableFeature("media_preview", + context.getString(R.string.enable_media_preview), + context.getString(R.string.enable_media_preview_sum), + SearchableFeature.Category.MEDIA, + SearchableFeature.FragmentType.MEDIA, + null, + Arrays.asList("media", "preview", "temporary"))); + } + + private static void addCustomizationFeatures(Context context, List catalog) { + catalog.add(new SearchableFeature("changecolor", + context.getString(R.string.colors_customization), + context.getString(R.string.colors_customization_sum), + SearchableFeature.Category.CUSTOMIZATION, + SearchableFeature.FragmentType.CUSTOMIZATION, + null, + Arrays.asList("colors", "customization", "theme"))); + + catalog.add(new SearchableFeature("primary_color", + context.getString(R.string.primary_color), + null, + SearchableFeature.Category.CUSTOMIZATION, + SearchableFeature.FragmentType.CUSTOMIZATION, + null, + Arrays.asList("primary", "color"))); + + catalog.add(new SearchableFeature("background_color", + context.getString(R.string.background_color), + null, + SearchableFeature.Category.CUSTOMIZATION, + SearchableFeature.FragmentType.CUSTOMIZATION, + null, + Arrays.asList("background", "color"))); + + catalog.add(new SearchableFeature("text_color", + context.getString(R.string.text_color), + null, + SearchableFeature.Category.CUSTOMIZATION, + SearchableFeature.FragmentType.CUSTOMIZATION, + null, + Arrays.asList("text", "color"))); + + catalog.add(new SearchableFeature("wallpaper", + context.getString(R.string.wallpaper_in_home_screen), + context.getString(R.string.wallpaper_in_home_screen_sum), + SearchableFeature.Category.CUSTOMIZATION, + SearchableFeature.FragmentType.CUSTOMIZATION, + null, + Arrays.asList("wallpaper", "background", "image"))); + + catalog.add(new SearchableFeature("hidetabs", + context.getString(R.string.hide_tabs_on_home), + context.getString(R.string.hide_tabs_on_home_sum), + SearchableFeature.Category.CUSTOMIZATION, + SearchableFeature.FragmentType.CUSTOMIZATION, + null, + Arrays.asList("hide", "tabs", "home"))); + + catalog.add(new SearchableFeature("custom_filters", + context.getString(R.string.custom_appearance), + context.getString(R.string.custom_filters_sum), + SearchableFeature.Category.CUSTOMIZATION, + SearchableFeature.FragmentType.CUSTOMIZATION, + null, + Arrays.asList("custom", "appearance", "filters", "css"))); + + catalog.add(new SearchableFeature("animation_list", + context.getString(R.string.list_animations_home_screen), + context.getString(R.string.list_animations_home_screen_sum), + SearchableFeature.Category.CUSTOMIZATION, + SearchableFeature.FragmentType.CUSTOMIZATION, + null, + Arrays.asList("animation", "list", "home"))); + + catalog.add(new SearchableFeature("admin_grp", + context.getString(R.string.show_admin_group_icon), + context.getString(R.string.show_admin_group_icon_sum), + SearchableFeature.Category.CUSTOMIZATION, + SearchableFeature.FragmentType.CUSTOMIZATION, + null, + Arrays.asList("admin", "group", "icon"))); + + catalog.add(new SearchableFeature("floatingmenu", + context.getString(R.string.new_context_menu_ui), + context.getString(R.string.new_context_menu_ui_sum), + SearchableFeature.Category.CUSTOMIZATION, + SearchableFeature.FragmentType.CUSTOMIZATION, + null, + Arrays.asList("floating", "menu", "context", "ios"))); + + catalog.add(new SearchableFeature("animation_emojis", + context.getString(R.string.animation_emojis), + context.getString(R.string.animation_emojis_sum), + SearchableFeature.Category.CUSTOMIZATION, + SearchableFeature.FragmentType.CUSTOMIZATION, + null, + Arrays.asList("animation", "emojis", "large"))); + + catalog.add(new SearchableFeature("bubble_color", + context.getString(R.string.change_bubble_colors), + context.getString(R.string.change_blubble_color_sum), + SearchableFeature.Category.CUSTOMIZATION, + SearchableFeature.FragmentType.CUSTOMIZATION, + null, + Arrays.asList("bubble", "color", "chat"))); + + catalog.add(new SearchableFeature("menuwicon", + context.getString(R.string.menuwicon), + context.getString(R.string.menuwicon_sum), + SearchableFeature.Category.CUSTOMIZATION, + SearchableFeature.FragmentType.CUSTOMIZATION, + null, + Arrays.asList("menu", "icons"))); + + catalog.add(new SearchableFeature("novaconfig", + context.getString(R.string.novaconfig), + context.getString(R.string.novaconfig_sum), + SearchableFeature.Category.CUSTOMIZATION, + SearchableFeature.FragmentType.CUSTOMIZATION, + null, + Arrays.asList("settings", "style", "profile"))); + + catalog.add(new SearchableFeature("igstatus", + context.getString(R.string.igstatus_on_home_screen), + context.getString(R.string.igstatus_on_home_screen_sum), + SearchableFeature.Category.CUSTOMIZATION, + SearchableFeature.FragmentType.CUSTOMIZATION, + null, + Arrays.asList("instagram", "status", "ig"))); + + catalog.add(new SearchableFeature("channels", + context.getString(R.string.disable_channels), + context.getString(R.string.disable_channels_sum), + SearchableFeature.Category.CUSTOMIZATION, + SearchableFeature.FragmentType.CUSTOMIZATION, + null, + Arrays.asList("channels", "disable", "hide"))); + + catalog.add(new SearchableFeature("removechannel_rec", + context.getString(R.string.remove_channel_recomendations), + context.getString(R.string.remove_channel_recomendations_sum), + SearchableFeature.Category.CUSTOMIZATION, + SearchableFeature.FragmentType.CUSTOMIZATION, + null, + Arrays.asList("channel", "recommendations", "remove"))); + + catalog.add(new SearchableFeature("status_style", + context.getString(R.string.style_of_stories_status), + context.getString(R.string.style_of_stories_status_sum), + SearchableFeature.Category.CUSTOMIZATION, + SearchableFeature.FragmentType.CUSTOMIZATION, + null, + Arrays.asList("status", "style", "stories"))); + + catalog.add(new SearchableFeature("oldstatus", + context.getString(R.string.old_statuses), + context.getString(R.string.old_statuses_sum), + SearchableFeature.Category.CUSTOMIZATION, + SearchableFeature.FragmentType.CUSTOMIZATION, + null, + Arrays.asList("old", "status", "vertical"))); + + catalog.add(new SearchableFeature("statuscomposer", + context.getString(R.string.custom_colors_for_text_status), + context.getString(R.string.custom_colors_for_text_status_sum), + SearchableFeature.Category.CUSTOMIZATION, + SearchableFeature.FragmentType.CUSTOMIZATION, + null, + Arrays.asList("status", "composer", "colors", "text"))); } - return getAllFeatures(context).stream() - .filter(feature -> feature.matches(query)) - .collect(Collectors.toList()); - } - - /** - * Build the complete feature catalog from all preference XMLs. - */ - private static List buildFeatureCatalog(Context context) { - List catalog = new ArrayList<>(); - - // GENERAL FRAGMENT - General sub-preferences - catalog.add(new SearchableFeature("thememode", - context.getString(R.string.theme_mode), - context.getString(R.string.theme_mode_sum), - SearchableFeature.Category.GENERAL_HOME, - SearchableFeature.FragmentType.GENERAL, - "general_home", - Arrays.asList("dark", "light", "theme"))); - - catalog.add(new SearchableFeature("update_check", - context.getString(R.string.update_check), - context.getString(R.string.update_check_sum), - SearchableFeature.Category.GENERAL_HOME, - SearchableFeature.FragmentType.GENERAL, - "general_home", - Arrays.asList("update", "check", "automatic"))); - - catalog.add(new SearchableFeature("disable_expiration", - context.getString(R.string.disable_whatsapp_expiration), - context.getString(R.string.disable_whatsapp_expiration_sum), - SearchableFeature.Category.GENERAL_HOME, - SearchableFeature.FragmentType.GENERAL, - "general_home", - Arrays.asList("expiration", "version"))); - - catalog.add(new SearchableFeature("force_restore_backup_feature", - context.getString(R.string.force_restore_backup), - context.getString(R.string.force_restore_backup_summary), - SearchableFeature.Category.GENERAL_HOME, - SearchableFeature.FragmentType.GENERAL, - "general_home", - Arrays.asList("backup", "restore", "force"))); - - catalog.add(new SearchableFeature("disable_ads", - context.getString(R.string.disable_ads), - context.getString(R.string.disable_ads_sum), - SearchableFeature.Category.GENERAL_HOME, - SearchableFeature.FragmentType.GENERAL, - "general_home", - Arrays.asList("ads", "advertising", "block"))); - - catalog.add(new SearchableFeature("lite_mode", - context.getString(R.string.lite_mode), - context.getString(R.string.lite_mode_sum), - SearchableFeature.Category.GENERAL_HOME, - SearchableFeature.FragmentType.GENERAL, - "general_home", - Arrays.asList("lite", "performance", "battery"))); - - catalog.add(new SearchableFeature("force_english", - context.getString(R.string.force_english), - null, - SearchableFeature.Category.GENERAL_HOME, - SearchableFeature.FragmentType.GENERAL, - "general_home", - Arrays.asList("english", "language"))); - - catalog.add(new SearchableFeature("enablelogs", - context.getString(R.string.verbose_logs), - null, - SearchableFeature.Category.GENERAL_HOME, - SearchableFeature.FragmentType.GENERAL, - "general_home", - Arrays.asList("logs", "debug", "verbose"))); - - catalog.add(new SearchableFeature("bypass_version_check", - context.getString(R.string.disable_version_check), - context.getString(R.string.disable_version_check_sum), - SearchableFeature.Category.GENERAL_HOME, - SearchableFeature.FragmentType.GENERAL, - "general_home", - Arrays.asList("version", "check", "bypass"))); - - catalog.add(new SearchableFeature("bootloader_spoofer", - context.getString(R.string.bootloader_spoofer), - context.getString(R.string.bootloader_spoofer_sum), - SearchableFeature.Category.GENERAL_HOME, - SearchableFeature.FragmentType.GENERAL, - "general_home", - Arrays.asList("bootloader", "spoofer", "ban"))); - - catalog.add(new SearchableFeature("ampm", - context.getString(R.string.ampm), - null, - SearchableFeature.Category.GENERAL_HOME, - SearchableFeature.FragmentType.GENERAL, - "general_home", - Arrays.asList("time", "12", "hour", "format"))); - - catalog.add(new SearchableFeature("segundos", - context.getString(R.string.segundosnahora), - context.getString(R.string.segundosnahora_sum), - SearchableFeature.Category.GENERAL_HOME, - SearchableFeature.FragmentType.GENERAL, - "general_home", - Arrays.asList("seconds", "timestamp", "time"))); - - catalog.add(new SearchableFeature("secondstotime", - context.getString(R.string.textonahora), - context.getString(R.string.textonahora_sum), - SearchableFeature.Category.GENERAL_HOME, - SearchableFeature.FragmentType.GENERAL, - "general_home", - Arrays.asList("text", "timestamp", "custom"))); - - catalog.add(new SearchableFeature("tasker", - context.getString(R.string.enable_tasker_automation), - context.getString(R.string.enable_tasker_automation_sum), - SearchableFeature.Category.GENERAL_HOME, - SearchableFeature.FragmentType.GENERAL, - "general_home", - Arrays.asList("tasker", "automation", "intent"))); - - // GENERAL FRAGMENT - Homescreen sub-preferences - catalog.add(new SearchableFeature("buttonaction", - context.getString(R.string.show_menu_buttons_as_icons), - context.getString(R.string.show_menu_buttons_as_icons_sum), - SearchableFeature.Category.GENERAL_HOMESCREEN, - SearchableFeature.FragmentType.GENERAL, - "homescreen", - Arrays.asList("menu", "icons", "buttons"))); - - catalog.add(new SearchableFeature("shownamehome", - context.getString(R.string.showname), - context.getString(R.string.showname_sum), - SearchableFeature.Category.GENERAL_HOMESCREEN, - SearchableFeature.FragmentType.GENERAL, - "homescreen", - Arrays.asList("name", "profile", "title"))); - - catalog.add(new SearchableFeature("showbiohome", - context.getString(R.string.showbio), - context.getString(R.string.showbio_sum), - SearchableFeature.Category.GENERAL_HOMESCREEN, - SearchableFeature.FragmentType.GENERAL, - "homescreen", - Arrays.asList("bio", "status", "toolbar"))); - - catalog.add(new SearchableFeature("show_dndmode", - context.getString(R.string.show_dnd_button), - context.getString(R.string.show_dnd_button_sum), - SearchableFeature.Category.GENERAL_HOMESCREEN, - SearchableFeature.FragmentType.GENERAL, - "homescreen", - Arrays.asList("dnd", "do not disturb", "button"))); - - catalog.add(new SearchableFeature("newchat", - context.getString(R.string.enable_new_chat_button), - context.getString(R.string.enable_new_chat_button_sum), - SearchableFeature.Category.GENERAL_HOMESCREEN, - SearchableFeature.FragmentType.GENERAL, - "homescreen", - Arrays.asList("new", "chat", "button"))); - - catalog.add(new SearchableFeature("restartbutton", - context.getString(R.string.enable_restart_button), - context.getString(R.string.enable_restart_button_sum), - SearchableFeature.Category.GENERAL_HOMESCREEN, - SearchableFeature.FragmentType.GENERAL, - "homescreen", - Arrays.asList("restart", "reboot", "button"))); - - catalog.add(new SearchableFeature("open_wae", - context.getString(R.string.enable_wa_enhancer_button), - context.getString(R.string.enable_wa_enhancer_button_sum), - SearchableFeature.Category.GENERAL_HOMESCREEN, - SearchableFeature.FragmentType.GENERAL, - "homescreen", - Arrays.asList("wa enhancer", "open", "button"))); - - catalog.add(new SearchableFeature("separategroups", - context.getString(R.string.separate_groups), - context.getString(R.string.separate_groups_sum), - SearchableFeature.Category.GENERAL_HOMESCREEN, - SearchableFeature.FragmentType.GENERAL, - "homescreen", - Arrays.asList("separate", "groups", "filter"))); - - catalog.add(new SearchableFeature("filtergroups", - context.getString(R.string.new_ui_group_filter), - context.getString(R.string.new_ui_group_filter_sum), - SearchableFeature.Category.GENERAL_HOMESCREEN, - SearchableFeature.FragmentType.GENERAL, - "homescreen", - Arrays.asList("filter", "groups", "ui"))); - - catalog.add(new SearchableFeature("dotonline", - context.getString(R.string.show_online_dot_in_conversation_list), - context.getString(R.string.show_online_dot_in_conversation_list_sum), - SearchableFeature.Category.GENERAL_HOMESCREEN, - SearchableFeature.FragmentType.GENERAL, - "homescreen", - Arrays.asList("online", "dot", "green"))); - - catalog.add(new SearchableFeature("showonlinetext", - context.getString(R.string.show_online_last_seen_in_conversation_list), - context.getString(R.string.show_online_last_seen_in_conversation_list_sum), - SearchableFeature.Category.GENERAL_HOMESCREEN, - SearchableFeature.FragmentType.GENERAL, - "homescreen", - Arrays.asList("online", "last seen", "text"))); - - catalog.add(new SearchableFeature("filterseen", - context.getString(R.string.enable_filter_chats), - context.getString(R.string.enable_filter_chats_sum), - SearchableFeature.Category.GENERAL_HOMESCREEN, - SearchableFeature.FragmentType.GENERAL, - "homescreen", - Arrays.asList("filter", "chats", "unseen"))); - - catalog.add(new SearchableFeature("metaai", - context.getString(R.string.disable_metaai), - context.getString(R.string.disable_metaai_sum), - SearchableFeature.Category.GENERAL_HOMESCREEN, - SearchableFeature.FragmentType.GENERAL, - "homescreen", - Arrays.asList("meta", "ai", "disable"))); - - catalog.add(new SearchableFeature("chatfilter", - context.getString(R.string.novofiltro), - context.getString(R.string.novofiltro_sum), - SearchableFeature.Category.GENERAL_HOMESCREEN, - SearchableFeature.FragmentType.GENERAL, - "homescreen", - Arrays.asList("search", "filter", "icon", "bar"))); - - catalog.add(new SearchableFeature("disable_profile_status", - context.getString(R.string.disable_status_in_the_profile_photo), - context.getString(R.string.disable_status_in_the_profile_photo_sum), - SearchableFeature.Category.GENERAL_HOMESCREEN, - SearchableFeature.FragmentType.GENERAL, - "homescreen", - Arrays.asList("status", "profile", "photo", "circle"))); - - // GENERAL FRAGMENT - Conversation sub-preferences - catalog.add(new SearchableFeature("showonline", - context.getString(R.string.show_toast_on_contact_online), - context.getString(R.string.show_toast_on_contact_online_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("toast", "online", "notification"))); - - catalog.add(new SearchableFeature("toastdeleted", - context.getString(R.string.toast_on_delete), - context.getString(R.string.toast_on_delete_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("toast", "deleted", "notification"))); - - catalog.add(new SearchableFeature("toast_viewed_message", - context.getString(R.string.toast_on_viewed_message), - context.getString(R.string.toast_on_viewed_message_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("toast", "viewed", "read", "notification"))); - - catalog.add(new SearchableFeature("antirevoke", - context.getString(R.string.antirevoke), - context.getString(R.string.antirevoke_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("anti", "revoke", "delete", "deleted"))); - - catalog.add(new SearchableFeature("antirevokestatus", - context.getString(R.string.antirevokestatus), - context.getString(R.string.antirevokestatus_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("anti", "delete", "status"))); - - catalog.add(new SearchableFeature("antidisappearing", - context.getString(R.string.antidisappearing), - context.getString(R.string.antidisappearing_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("anti", "disappearing", "temporary", "messages"))); - - catalog.add(new SearchableFeature("broadcast_tag", - context.getString(R.string.show_chat_broadcast_icon), - context.getString(R.string.show_chat_broadcast_icon_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("broadcast", "icon", "tag"))); - - catalog.add(new SearchableFeature("pinnedlimit", - context.getString(R.string.disable_pinned_limit), - context.getString(R.string.disable_pinned_limit_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("pinned", "limit", "chats"))); - - catalog.add(new SearchableFeature("removeforwardlimit", - context.getString(R.string.removeforwardlimit), - context.getString(R.string.removeforwardlimit_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("forward", "limit", "remove"))); - - catalog.add(new SearchableFeature("hidetag", - context.getString(R.string.hidetag), - context.getString(R.string.hidetag_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("forwarded", "tag", "hide"))); - - catalog.add(new SearchableFeature("revokeallmessages", - context.getString(R.string.delete_for_everyone_all_messages), - context.getString(R.string.delete_for_everyone_all_messages_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("delete", "everyone", "limit", "revoke"))); - - catalog.add(new SearchableFeature("removeseemore", - context.getString(R.string.remove_see_more_button), - context.getString(R.string.remove_see_more_button_), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("see more", "button", "remove"))); - - catalog.add(new SearchableFeature("antieditmessages", - context.getString(R.string.show_edited_message_history), - context.getString(R.string.show_edited_message_history_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("edited", "history", "message"))); - - catalog.add(new SearchableFeature("alertsticker", - context.getString(R.string.enable_confirmation_to_send_sticker), - context.getString(R.string.enable_confirmation_to_send_sticker_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("sticker", "confirmation", "alert"))); - - catalog.add(new SearchableFeature("calltype", - context.getString(R.string.selection_of_call_type), - context.getString(R.string.selection_of_call_type_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("call", "type", "selection", "phone"))); - - catalog.add(new SearchableFeature("disable_defemojis", - context.getString(R.string.disable_default_emojis), - context.getString(R.string.disable_default_emojis_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("emoji", "default", "disable"))); - - catalog.add(new SearchableFeature("stamp_copied_message", - context.getString(R.string.stamp_copied_messages), - context.getString(R.string.stamp_copied_messages_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("copy", "stamp", "copied", "messages"))); - - catalog.add(new SearchableFeature("doubletap2like", - context.getString(R.string.double_click_to_react), - context.getString(R.string.double_click_to_like_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("double", "tap", "click", "react", "like"))); - - catalog.add(new SearchableFeature("doubletap2like_emoji", - context.getString(R.string.custom_reaction), - context.getString(R.string.custom_reaction_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("reaction", "emoji", "custom"))); - - catalog.add(new SearchableFeature("google_translate", - context.getString(R.string.google_translate), - context.getString(R.string.google_translate_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("translate", "google", "language"))); - - catalog.add(new SearchableFeature("deleted_messages_activity", - context.getString(R.string.deleted_messages_title), - context.getString(R.string.deleted_messages_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.ACTIVITY, - null, - Arrays.asList("deleted", "messages", "restore", "history", "log"))); - - catalog.add(new SearchableFeature("verify_blocked_contact", - context.getString(R.string.show_contact_blocked), - context.getString(R.string.show_contact_blocked_sum), - SearchableFeature.Category.GENERAL_CONVERSATION, - SearchableFeature.FragmentType.GENERAL, - "conversation", - Arrays.asList("blocked", "contact", "verify"))); - - // GENERAL FRAGMENT - Status - catalog.add(new SearchableFeature("autonext_status", - context.getString(R.string.disable_auto_status), - context.getString(R.string.disable_auto_status_sum), - SearchableFeature.Category.GENERAL, - SearchableFeature.FragmentType.GENERAL, - null, - Arrays.asList("auto", "status", "skip"))); - - catalog.add(new SearchableFeature("copystatus", - context.getString(R.string.enable_copy_status), - context.getString(R.string.enable_copy_status_sum), - SearchableFeature.Category.GENERAL, - SearchableFeature.FragmentType.GENERAL, - null, - Arrays.asList("copy", "status", "caption"))); - - catalog.add(new SearchableFeature("toast_viewed_status", - context.getString(R.string.toast_on_viewed_status), - context.getString(R.string.toast_on_viewed_status_sum), - SearchableFeature.Category.GENERAL, - SearchableFeature.FragmentType.GENERAL, - null, - Arrays.asList("toast", "viewed", "status", "notification"))); - - // PRIVACY FRAGMENT - catalog.add(new SearchableFeature("typearchive", - context.getString(R.string.hide_archived_chat), - context.getString(R.string.hide_archived_chat_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("archive", "hide", "hidden"))); - - catalog.add(new SearchableFeature("show_freezeLastSeen", - context.getString(R.string.show_freezeLastSeen_button), - context.getString(R.string.show_freezeLastSeen_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("freeze", "last seen", "button"))); - - catalog.add(new SearchableFeature("ghostmode", - context.getString(R.string.ghost_mode_title), - context.getString(R.string.ghost_mode_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("ghost", "mode", "invisible"))); - - catalog.add(new SearchableFeature("always_online", - context.getString(R.string.always_online), - context.getString(R.string.always_online_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("always", "online", "status"))); - - catalog.add(new SearchableFeature("lockedchats_enhancer", - context.getString(R.string.lockedchats_enhancer), - context.getString(R.string.lockedchats_enhancer_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("locked", "chats", "enhanced"))); - - catalog.add(new SearchableFeature("custom_privacy_type", - context.getString(R.string.custom_privacy_per_contact), - context.getString(R.string.custom_privacy_per_contact_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("custom", "privacy", "contact"))); - - catalog.add(new SearchableFeature("freezelastseen", - context.getString(R.string.freezelastseen), - context.getString(R.string.freezelastseen_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("freeze", "last seen"))); - - catalog.add(new SearchableFeature("hideread", - context.getString(R.string.hideread), - context.getString(R.string.hideread_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("hide", "read", "blue", "ticks"))); - - catalog.add(new SearchableFeature("hide_seen_view", - context.getString(R.string.view_seen_tick), - context.getString(R.string.view_seen_tick_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("view", "seen", "tick"))); - - catalog.add(new SearchableFeature("blueonreply", - context.getString(R.string.blueonreply), - context.getString(R.string.blueonreply_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("blue", "tick", "reply"))); - - catalog.add(new SearchableFeature("hideread_group", - context.getString(R.string.hideread_group), - context.getString(R.string.hideread_group_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("hide", "read", "group", "ticks"))); - - catalog.add(new SearchableFeature("hidereceipt", - context.getString(R.string.hidereceipt), - context.getString(R.string.hidereceipt_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("hide", "delivered", "receipt"))); - - catalog.add(new SearchableFeature("ghostmode_t", - context.getString(R.string.ghostmode), - context.getString(R.string.ghostmode_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("ghost", "typing", "hide"))); - - catalog.add(new SearchableFeature("ghostmode_r", - context.getString(R.string.ghostmode_r), - context.getString(R.string.ghostmode_sum_r), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("ghost", "recording", "audio"))); - - catalog.add(new SearchableFeature("hideonceseen", - context.getString(R.string.hide_once_view_seen), - context.getString(R.string.hide_once_view_seen_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("view", "once", "seen", "hide"))); - - catalog.add(new SearchableFeature("hideaudioseen", - context.getString(R.string.hide_audio_seen), - context.getString(R.string.hide_audio_seen_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("audio", "seen", "hide"))); - - catalog.add(new SearchableFeature("viewonce", - context.getString(R.string.viewonce), - context.getString(R.string.viewonce_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("view", "once", "unlimited"))); - - catalog.add(new SearchableFeature("seentick", - context.getString(R.string.show_button_to_send_blue_tick), - context.getString(R.string.show_button_to_send_blue_tick_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("blue", "tick", "button", "mark", "read"))); - - catalog.add(new SearchableFeature("hidestatusview", - context.getString(R.string.hidestatusview), - context.getString(R.string.hidestatusview_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("status", "view", "hide"))); - - catalog.add(new SearchableFeature("call_info", - context.getString(R.string.additional_call_information), - context.getString(R.string.additional_call_information_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("call", "information", "additional"))); - - catalog.add(new SearchableFeature("call_privacy", - context.getString(R.string.call_blocker), - context.getString(R.string.call_blocker_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("call", "blocker", "block"))); - - catalog.add(new SearchableFeature("call_type", - context.getString(R.string.call_blocking_type), - context.getString(R.string.call_blocking_type_sum), - SearchableFeature.Category.PRIVACY, - SearchableFeature.FragmentType.PRIVACY, - null, - Arrays.asList("call", "blocking", "type"))); - - // Continue in next part... - addMediaFeatures(context, catalog); - addCustomizationFeatures(context, catalog); - addHomeActions(context, catalog); - - return catalog; - } - - private static void addMediaFeatures(Context context, List catalog) { - catalog.add(new SearchableFeature("imagequality", - context.getString(R.string.imagequality), - context.getString(R.string.imagequality_sum), - SearchableFeature.Category.MEDIA, - SearchableFeature.FragmentType.MEDIA, - null, - Arrays.asList("image", "quality", "hd"))); - - catalog.add(new SearchableFeature("download_local", - context.getString(R.string.local_download), - null, - SearchableFeature.Category.MEDIA, - SearchableFeature.FragmentType.MEDIA, - null, - Arrays.asList("download", "local", "folder"))); - - catalog.add(new SearchableFeature("downloadstatus", - context.getString(R.string.statusdowload), - context.getString(R.string.statusdowload_sum), - SearchableFeature.Category.MEDIA, - SearchableFeature.FragmentType.MEDIA, - null, - Arrays.asList("download", "status", "share"))); - - catalog.add(new SearchableFeature("downloadviewonce", - context.getString(R.string.downloadviewonce), - context.getString(R.string.downloadviewonce_sum), - SearchableFeature.Category.MEDIA, - SearchableFeature.FragmentType.MEDIA, - null, - Arrays.asList("download", "view", "once"))); - - catalog.add(new SearchableFeature("video_limit_size", - context.getString(R.string.increase_video_size_limit), - null, - SearchableFeature.Category.MEDIA, - SearchableFeature.FragmentType.MEDIA, - null, - Arrays.asList("video", "size", "limit", "mb"))); - - catalog.add(new SearchableFeature("videoquality", - context.getString(R.string.videoquality), - context.getString(R.string.videoquality_sum), - SearchableFeature.Category.MEDIA, - SearchableFeature.FragmentType.MEDIA, - null, - Arrays.asList("video", "quality", "hd"))); - - catalog.add(new SearchableFeature("video_real_resolution", - context.getString(R.string.send_video_in_real_resolution), - context.getString(R.string.send_video_in_real_resolution_sum), - SearchableFeature.Category.MEDIA, - SearchableFeature.FragmentType.MEDIA, - null, - Arrays.asList("video", "resolution", "real"))); - - catalog.add(new SearchableFeature("video_maxfps", - context.getString(R.string.send_video_in_60fps), - context.getString(R.string.send_video_in_60fps_sum), - SearchableFeature.Category.MEDIA, - SearchableFeature.FragmentType.MEDIA, - null, - Arrays.asList("video", "60fps", "fps"))); - - catalog.add(new SearchableFeature("call_recording_enable", - context.getString(R.string.call_recording_enable), - context.getString(R.string.call_recording_enable_sum), - SearchableFeature.Category.MEDIA, - SearchableFeature.FragmentType.MEDIA, - null, - Arrays.asList("call", "recording", "record"))); - - catalog.add(new SearchableFeature("call_recording_path", - context.getString(R.string.call_recording_path), - null, - SearchableFeature.Category.MEDIA, - SearchableFeature.FragmentType.MEDIA, - null, - Arrays.asList("recording", "path", "folder"))); - - catalog.add(new SearchableFeature("call_recording_toast", - context.getString(R.string.call_recording_toast_title), - context.getString(R.string.call_recording_toast_summary), - SearchableFeature.Category.MEDIA, - SearchableFeature.FragmentType.MEDIA, - null, - Arrays.asList("recording", "toast", "notification", "show", "hide"))); - - catalog.add(new SearchableFeature("disable_sensor_proximity", - context.getString(R.string.disable_the_proximity_sensor), - context.getString(R.string.disable_the_proximity_sensor_sum), - SearchableFeature.Category.MEDIA, - SearchableFeature.FragmentType.MEDIA, - null, - Arrays.asList("proximity", "sensor", "screen"))); - - catalog.add(new SearchableFeature("proximity_audios", - context.getString(R.string.disable_audio_sensor), - context.getString(R.string.disable_audio_sensor_sum), - SearchableFeature.Category.MEDIA, - SearchableFeature.FragmentType.MEDIA, - null, - Arrays.asList("audio", "proximity", "sensor"))); - - catalog.add(new SearchableFeature("audio_type", - context.getString(R.string.send_audio_as_voice_audio_note), - context.getString(R.string.send_audio_as_voice_audio_note_sum), - SearchableFeature.Category.MEDIA, - SearchableFeature.FragmentType.MEDIA, - null, - Arrays.asList("audio", "voice", "note"))); - - catalog.add(new SearchableFeature("voicenote_speed", - context.getString(R.string.voice_note_speed), - null, - SearchableFeature.Category.MEDIA, - SearchableFeature.FragmentType.MEDIA, - null, - Arrays.asList("voice", "note", "speed"))); - - catalog.add(new SearchableFeature("audio_transcription", - context.getString(R.string.audio_transcription), - context.getString(R.string.audio_transcription_sum), - SearchableFeature.Category.MEDIA, - SearchableFeature.FragmentType.MEDIA, - null, - Arrays.asList("audio", "transcription", "text"))); - - catalog.add(new SearchableFeature("transcription_provider", - context.getString(R.string.transcription_provider), - context.getString(R.string.transcription_provider_sum), - SearchableFeature.Category.MEDIA, - SearchableFeature.FragmentType.MEDIA, - null, - Arrays.asList("transcription", "provider", "ai"))); - - catalog.add(new SearchableFeature("media_preview", - context.getString(R.string.enable_media_preview), - context.getString(R.string.enable_media_preview_sum), - SearchableFeature.Category.MEDIA, - SearchableFeature.FragmentType.MEDIA, - null, - Arrays.asList("media", "preview", "temporary"))); - } - - private static void addCustomizationFeatures(Context context, List catalog) { - catalog.add(new SearchableFeature("changecolor", - context.getString(R.string.colors_customization), - context.getString(R.string.colors_customization_sum), - SearchableFeature.Category.CUSTOMIZATION, - SearchableFeature.FragmentType.CUSTOMIZATION, - null, - Arrays.asList("colors", "customization", "theme"))); - - catalog.add(new SearchableFeature("primary_color", - context.getString(R.string.primary_color), - null, - SearchableFeature.Category.CUSTOMIZATION, - SearchableFeature.FragmentType.CUSTOMIZATION, - null, - Arrays.asList("primary", "color"))); - - catalog.add(new SearchableFeature("background_color", - context.getString(R.string.background_color), - null, - SearchableFeature.Category.CUSTOMIZATION, - SearchableFeature.FragmentType.CUSTOMIZATION, - null, - Arrays.asList("background", "color"))); - - catalog.add(new SearchableFeature("text_color", - context.getString(R.string.text_color), - null, - SearchableFeature.Category.CUSTOMIZATION, - SearchableFeature.FragmentType.CUSTOMIZATION, - null, - Arrays.asList("text", "color"))); - - catalog.add(new SearchableFeature("wallpaper", - context.getString(R.string.wallpaper_in_home_screen), - context.getString(R.string.wallpaper_in_home_screen_sum), - SearchableFeature.Category.CUSTOMIZATION, - SearchableFeature.FragmentType.CUSTOMIZATION, - null, - Arrays.asList("wallpaper", "background", "image"))); - - catalog.add(new SearchableFeature("hidetabs", - context.getString(R.string.hide_tabs_on_home), - context.getString(R.string.hide_tabs_on_home_sum), - SearchableFeature.Category.CUSTOMIZATION, - SearchableFeature.FragmentType.CUSTOMIZATION, - null, - Arrays.asList("hide", "tabs", "home"))); - - catalog.add(new SearchableFeature("custom_filters", - context.getString(R.string.custom_appearance), - context.getString(R.string.custom_filters_sum), - SearchableFeature.Category.CUSTOMIZATION, - SearchableFeature.FragmentType.CUSTOMIZATION, - null, - Arrays.asList("custom", "appearance", "filters", "css"))); - - catalog.add(new SearchableFeature("animation_list", - context.getString(R.string.list_animations_home_screen), - context.getString(R.string.list_animations_home_screen_sum), - SearchableFeature.Category.CUSTOMIZATION, - SearchableFeature.FragmentType.CUSTOMIZATION, - null, - Arrays.asList("animation", "list", "home"))); - - catalog.add(new SearchableFeature("admin_grp", - context.getString(R.string.show_admin_group_icon), - context.getString(R.string.show_admin_group_icon_sum), - SearchableFeature.Category.CUSTOMIZATION, - SearchableFeature.FragmentType.CUSTOMIZATION, - null, - Arrays.asList("admin", "group", "icon"))); - - catalog.add(new SearchableFeature("floatingmenu", - context.getString(R.string.new_context_menu_ui), - context.getString(R.string.new_context_menu_ui_sum), - SearchableFeature.Category.CUSTOMIZATION, - SearchableFeature.FragmentType.CUSTOMIZATION, - null, - Arrays.asList("floating", "menu", "context", "ios"))); - - catalog.add(new SearchableFeature("animation_emojis", - context.getString(R.string.animation_emojis), - context.getString(R.string.animation_emojis_sum), - SearchableFeature.Category.CUSTOMIZATION, - SearchableFeature.FragmentType.CUSTOMIZATION, - null, - Arrays.asList("animation", "emojis", "large"))); - - catalog.add(new SearchableFeature("bubble_color", - context.getString(R.string.change_bubble_colors), - context.getString(R.string.change_blubble_color_sum), - SearchableFeature.Category.CUSTOMIZATION, - SearchableFeature.FragmentType.CUSTOMIZATION, - null, - Arrays.asList("bubble", "color", "chat"))); - - catalog.add(new SearchableFeature("menuwicon", - context.getString(R.string.menuwicon), - context.getString(R.string.menuwicon_sum), - SearchableFeature.Category.CUSTOMIZATION, - SearchableFeature.FragmentType.CUSTOMIZATION, - null, - Arrays.asList("menu", "icons"))); - - catalog.add(new SearchableFeature("novaconfig", - context.getString(R.string.novaconfig), - context.getString(R.string.novaconfig_sum), - SearchableFeature.Category.CUSTOMIZATION, - SearchableFeature.FragmentType.CUSTOMIZATION, - null, - Arrays.asList("settings", "style", "profile"))); - - catalog.add(new SearchableFeature("igstatus", - context.getString(R.string.igstatus_on_home_screen), - context.getString(R.string.igstatus_on_home_screen_sum), - SearchableFeature.Category.CUSTOMIZATION, - SearchableFeature.FragmentType.CUSTOMIZATION, - null, - Arrays.asList("instagram", "status", "ig"))); - - catalog.add(new SearchableFeature("channels", - context.getString(R.string.disable_channels), - context.getString(R.string.disable_channels_sum), - SearchableFeature.Category.CUSTOMIZATION, - SearchableFeature.FragmentType.CUSTOMIZATION, - null, - Arrays.asList("channels", "disable", "hide"))); - - catalog.add(new SearchableFeature("removechannel_rec", - context.getString(R.string.remove_channel_recomendations), - context.getString(R.string.remove_channel_recomendations_sum), - SearchableFeature.Category.CUSTOMIZATION, - SearchableFeature.FragmentType.CUSTOMIZATION, - null, - Arrays.asList("channel", "recommendations", "remove"))); - - catalog.add(new SearchableFeature("status_style", - context.getString(R.string.style_of_stories_status), - context.getString(R.string.style_of_stories_status_sum), - SearchableFeature.Category.CUSTOMIZATION, - SearchableFeature.FragmentType.CUSTOMIZATION, - null, - Arrays.asList("status", "style", "stories"))); - - catalog.add(new SearchableFeature("oldstatus", - context.getString(R.string.old_statuses), - context.getString(R.string.old_statuses_sum), - SearchableFeature.Category.CUSTOMIZATION, - SearchableFeature.FragmentType.CUSTOMIZATION, - null, - Arrays.asList("old", "status", "vertical"))); - - catalog.add(new SearchableFeature("statuscomposer", - context.getString(R.string.custom_colors_for_text_status), - context.getString(R.string.custom_colors_for_text_status_sum), - SearchableFeature.Category.CUSTOMIZATION, - SearchableFeature.FragmentType.CUSTOMIZATION, - null, - Arrays.asList("status", "composer", "colors", "text"))); - } - - private static void addHomeActions(Context context, List catalog) { - // Home Fragment Actions - catalog.add(new SearchableFeature("export_config", - context.getString(R.string.export_settings), - context.getString(R.string.backup_settings), - SearchableFeature.Category.HOME_ACTIONS, - SearchableFeature.FragmentType.HOME, - null, - Arrays.asList("export", "backup", "settings", "config"))); - - catalog.add(new SearchableFeature("import_config", - context.getString(R.string.import_settings), - context.getString(R.string.backup_settings), - SearchableFeature.Category.HOME_ACTIONS, - SearchableFeature.FragmentType.HOME, - null, - Arrays.asList("import", "restore", "settings", "config"))); - - catalog.add(new SearchableFeature("reset_config", - context.getString(R.string.reset_settings), - null, - SearchableFeature.Category.HOME_ACTIONS, - SearchableFeature.FragmentType.HOME, - null, - Arrays.asList("reset", "settings", "clear"))); - - catalog.add(new SearchableFeature("reboot_wpp", - context.getString(R.string.restart_whatsapp), - null, - SearchableFeature.Category.HOME_ACTIONS, - SearchableFeature.FragmentType.HOME, - null, - Arrays.asList("restart", "reboot", "whatsapp", "refresh"))); - } + private static void addHomeActions(Context context, List catalog) { + // Home Fragment Actions + catalog.add(new SearchableFeature("export_config", + context.getString(R.string.export_settings), + context.getString(R.string.backup_settings), + SearchableFeature.Category.HOME_ACTIONS, + SearchableFeature.FragmentType.HOME, + null, + Arrays.asList("export", "backup", "settings", "config"))); + + catalog.add(new SearchableFeature("import_config", + context.getString(R.string.import_settings), + context.getString(R.string.backup_settings), + SearchableFeature.Category.HOME_ACTIONS, + SearchableFeature.FragmentType.HOME, + null, + Arrays.asList("import", "restore", "settings", "config"))); + + catalog.add(new SearchableFeature("reset_config", + context.getString(R.string.reset_settings), + null, + SearchableFeature.Category.HOME_ACTIONS, + SearchableFeature.FragmentType.HOME, + null, + Arrays.asList("reset", "settings", "clear"))); + + catalog.add(new SearchableFeature("reboot_wpp", + context.getString(R.string.restart_whatsapp), + null, + SearchableFeature.Category.HOME_ACTIONS, + SearchableFeature.FragmentType.HOME, + null, + Arrays.asList("restart", "reboot", "whatsapp", "refresh"))); + } } diff --git a/app/src/main/java/com/wmods/wppenhacer/xposed/features/general/VideoNoteAttachment.java b/app/src/main/java/com/wmods/wppenhacer/xposed/features/general/VideoNoteAttachment.java new file mode 100644 index 000000000..90b5cca25 --- /dev/null +++ b/app/src/main/java/com/wmods/wppenhacer/xposed/features/general/VideoNoteAttachment.java @@ -0,0 +1,301 @@ +package com.wmods.wppenhacer.xposed.features.general; + +import android.app.Activity; +import android.content.Intent; +import android.graphics.Color; +import android.graphics.drawable.GradientDrawable; +import android.net.Uri; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; + +import com.wmods.wppenhacer.xposed.core.Feature; +import com.wmods.wppenhacer.xposed.core.WppCore; +import com.wmods.wppenhacer.xposed.core.devkit.Unobfuscator; +import com.wmods.wppenhacer.xposed.utils.Utils; + +import de.robv.android.xposed.XC_MethodHook; +import de.robv.android.xposed.XSharedPreferences; +import de.robv.android.xposed.XposedBridge; +import de.robv.android.xposed.XposedHelpers; + +public class VideoNoteAttachment extends Feature { + + public static final int REQUEST_PICK_VIDEO_NOTE = 0x8891; + + public VideoNoteAttachment(ClassLoader loader, XSharedPreferences preferences) { + super(loader, preferences); + } + + @Override + public void doHook() throws Throwable { + if (!prefs.getBoolean("video_note_attachment", false)) + return; + + // ── 1. Inject "Video Note" button into the attachment picker ──────────── + final int rowId = Utils.getID("attach_picker_row", "id"); + + XposedBridge.hookAllMethods(ViewGroup.class, "addView", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + View added = (View) param.args[0]; + if (added == null || rowId <= 0 || added.getId() != rowId) + return; + ViewGroup parent = (ViewGroup) param.thisObject; + + added.post(() -> { + try { + if (parent.findViewWithTag("wae_videonote") != null) + return; + + // Find last attach_picker_row inside parent + ViewGroup lastRow = null; + ViewGroup firstRow = null; + for (int i = 0; i < parent.getChildCount(); i++) { + View child = parent.getChildAt(i); + if (child.getId() == rowId && child instanceof ViewGroup) { + if (firstRow == null) + firstRow = (ViewGroup) child; + lastRow = (ViewGroup) child; + } + } + if (lastRow == null) + return; + + // Get exact item size from a known button in row 1 + int btnW = Utils.dipToPixels(88); + int btnH = Utils.dipToPixels(140); + if (firstRow != null && firstRow.getChildCount() > 0) { + View ref = firstRow.getChildAt(0); + if (ref.getWidth() > 0) + btnW = ref.getWidth(); + if (ref.getHeight() > 0) + btnH = ref.getHeight(); + } + injectVideoNoteItem(lastRow, btnW, btnH); + } catch (Exception e) { + // Ignore + } + }); + } + }); + + // ── 2. Intercept the Activity result when the user picks a video ──────── + // ActivityController filters by a proxy class, so we hook separately here. + XposedHelpers.findAndHookMethod(Activity.class, "onActivityResult", + int.class, int.class, Intent.class, new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + int requestCode = (int) param.args[0]; + if (requestCode != REQUEST_PICK_VIDEO_NOTE) + return; + + int resultCode = (int) param.args[1]; + Intent data = (Intent) param.args[2]; + if (resultCode != Activity.RESULT_OK || data == null) { + return; + } + + Uri videoUri = data.getData(); + sendVideoToConversation((Activity) param.thisObject, videoUri); + } + }); + // ── 3. Force mediaType 81 (PTV) on message creation ──────────── + try { + Class abstractMediaMessageClass = Unobfuscator.loadAbstractMediaMessageClass(classLoader); + if (abstractMediaMessageClass != null) { + XposedBridge.hookAllConstructors(abstractMediaMessageClass, new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + Object fMessage = param.thisObject; + com.wmods.wppenhacer.xposed.core.components.FMessageWpp wrappedMsg = new com.wmods.wppenhacer.xposed.core.components.FMessageWpp( + fMessage); + + int mediaType = wrappedMsg.getMediaType(); + // Video mediaType is normally 3. If we intercept construction of a video, + // we'll check if it's meant to be a Video Note (e.g. via our "Video Note" + // button) + if (mediaType == 3) { + java.io.File mediaFile = wrappedMsg.getMediaFile(); + if (mediaFile != null + && mediaFile.getAbsolutePath().toLowerCase().contains("whatsapp video notes")) { + java.lang.reflect.Field mediaTypeField = Unobfuscator.loadMediaTypeField(classLoader); + mediaTypeField.setAccessible(true); + mediaTypeField.setInt(fMessage, 81); + } else { + // Also check our custom "wae_force_ptv" static flag that we can set during + // picking + if (VideoNoteAttachment.FORCE_NEXT_VIDEO_AS_PTV) { + java.lang.reflect.Field mediaTypeField = Unobfuscator + .loadMediaTypeField(classLoader); + mediaTypeField.setAccessible(true); + mediaTypeField.setInt(fMessage, 81); + + // Reset the flag after consuming it + VideoNoteAttachment.FORCE_NEXT_VIDEO_AS_PTV = false; + } + } + } + } + }); + } + } catch (Exception e) { + // Ignore + } + } + + // A flag to communicate between the picker result and the message builder + public static boolean FORCE_NEXT_VIDEO_AS_PTV = false; + + // ── Inject our item into the last row ────────────────────────────────────── + private void injectVideoNoteItem(ViewGroup row, int btnW, int btnH) { + Activity activity = WppCore.getCurrentActivity(); + if (activity == null) + return; + + boolean isDark = (row.getContext().getResources().getConfiguration().uiMode + & android.content.res.Configuration.UI_MODE_NIGHT_MASK) == android.content.res.Configuration.UI_MODE_NIGHT_YES; + int bgColor = isDark ? Color.parseColor("#233138") : Color.WHITE; + int strokeColor = isDark ? Color.parseColor("#344A55") : Color.parseColor("#E1E4E8"); + int textColor = isDark ? Color.parseColor("#E9EDF0") : Color.parseColor("#4B5563"); + + LinearLayout item = new LinearLayout(row.getContext()); + item.setOrientation(LinearLayout.VERTICAL); + // Center contents properly in the grid cell + item.setGravity(Gravity.CENTER); + item.setClickable(true); + item.setFocusable(true); + item.setTag("wae_videonote"); + // Restore btnH so it matches the other grid items' strict bounds + item.setLayoutParams(new LinearLayout.LayoutParams(btnW, btnH)); + // Remove manual padding to let Gravity.CENTER handle alignment + item.setPadding(0, 0, 0, 0); + + // Icon — rounded square matching WhatsApp's style + int squareSize = Utils.dipToPixels(54); + ImageView icon = new ImageView(row.getContext()); + LinearLayout.LayoutParams iconLp = new LinearLayout.LayoutParams(squareSize, squareSize); + iconLp.gravity = Gravity.CENTER_HORIZONTAL; + icon.setLayoutParams(iconLp); + icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE); + icon.setPadding(Utils.dipToPixels(12), Utils.dipToPixels(12), + Utils.dipToPixels(12), Utils.dipToPixels(12)); + + GradientDrawable bg = new GradientDrawable(); + bg.setShape(GradientDrawable.RECTANGLE); + bg.setCornerRadius(Utils.dipToPixels(18)); // Slightly rounder, 18dp is common for WA Squircles + bg.setColor(bgColor); + bg.setStroke(Utils.dipToPixels(1), strokeColor); + icon.setBackground(bg); + + int drawableId = Utils.getID("ic_attach_video", "drawable"); + if (drawableId <= 0) + drawableId = Utils.getID("ic_video", "drawable"); + if (drawableId <= 0) + drawableId = android.R.drawable.ic_menu_camera; + icon.setImageResource(drawableId); + icon.setColorFilter(Color.parseColor("#E91E8C")); // Magenta-pink icon color + + // Label + TextView label = new TextView(row.getContext()); + label.setText("Video Note"); + label.setTextSize(12f); + label.setTextColor(textColor); + label.setGravity(Gravity.CENTER_HORIZONTAL); + label.setMaxLines(1); + label.setSingleLine(true); + LinearLayout.LayoutParams labelLp = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); + labelLp.topMargin = Utils.dipToPixels(6); + labelLp.gravity = Gravity.CENTER_HORIZONTAL; + label.setLayoutParams(labelLp); + + item.addView(icon); + item.addView(label); + + item.setOnClickListener(v -> { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType("video/*"); + intent.addCategory(Intent.CATEGORY_OPENABLE); + activity.startActivityForResult( + Intent.createChooser(intent, "Select Video for Video Note"), + REQUEST_PICK_VIDEO_NOTE); + }); + + row.addView(item); + } + + // ── Send the picked video to the current conversation ───────────────────── + private static void sendVideoToConversation(Activity activity, Uri videoUri) { + try { + // Grant WhatsApp read permission on the URI + activity.grantUriPermission("com.whatsapp", videoUri, + Intent.FLAG_GRANT_READ_URI_PERMISSION); + + // Determine if we are in a WhatsApp conversation by checking the intent JID + Intent conversationIntent = activity.getIntent(); + String jid = conversationIntent != null + ? conversationIntent.getStringExtra("jid") + : null; + + // Arm the PTV override hook! + VideoNoteAttachment.FORCE_NEXT_VIDEO_AS_PTV = true; + + if (jid != null) { + // We're in a conversation - share the video directly to this chat. + Intent send = new Intent(Intent.ACTION_SEND); + send.setType("video/mp4"); + send.setPackage("com.whatsapp"); + send.putExtra(Intent.EXTRA_STREAM, videoUri); + send.putExtra("jid", jid); + // Magic flags from WhatsApp intent inspections + send.putExtra("is_media_ptv", true); + send.putExtra("origin", 5); + send.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + + activity.startActivity(send); + } else { + // Fallback: open WhatsApp share sheet + Intent send = new Intent(Intent.ACTION_SEND); + send.setType("video/*"); + send.setPackage("com.whatsapp"); + send.putExtra(Intent.EXTRA_STREAM, videoUri); + // Magic flags from WhatsApp intent inspections + send.putExtra("is_media_ptv", true); + send.putExtra("origin", 5); + send.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + activity.startActivity( + Intent.createChooser(send, "Send Video Note via WhatsApp")); + } + + Utils.showToast("Preparing Video Note…", Toast.LENGTH_SHORT); + } catch (Exception e) { + XposedBridge.log("VideoNoteAttachment sendVideo: " + e.getMessage()); + Utils.showToast("Error: " + e.getMessage(), Toast.LENGTH_LONG); + } + } + + /** + * Called by ActivityController if it handles the result separately (kept for + * compat). + */ + public static void handleVideoPicked(Uri videoUri) { + if (videoUri == null) + return; + Activity activity = WppCore.getCurrentActivity(); + if (activity != null) + sendVideoToConversation(activity, videoUri); + } + + @NonNull + @Override + public String getPluginName() { + return "Video Note Attachment"; + } +} diff --git a/app/src/main/res/drawable/bottom_sheet_bg.xml b/app/src/main/res/drawable/bottom_sheet_bg.xml new file mode 100644 index 000000000..3eb10bacd --- /dev/null +++ b/app/src/main/res/drawable/bottom_sheet_bg.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/app/src/main/res/drawable/drag_handle_bg.xml b/app/src/main/res/drawable/drag_handle_bg.xml new file mode 100644 index 000000000..94f1083e7 --- /dev/null +++ b/app/src/main/res/drawable/drag_handle_bg.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_arrow_back.xml b/app/src/main/res/drawable/ic_arrow_back.xml new file mode 100644 index 000000000..bab545a70 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_back.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_language.xml b/app/src/main/res/drawable/ic_language.xml new file mode 100644 index 000000000..ff1be8648 --- /dev/null +++ b/app/src/main/res/drawable/ic_language.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_location_pin.xml b/app/src/main/res/drawable/ic_location_pin.xml new file mode 100644 index 000000000..958bf09cb --- /dev/null +++ b/app/src/main/res/drawable/ic_location_pin.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_twitter.xml b/app/src/main/res/drawable/ic_twitter.xml new file mode 100644 index 000000000..1cf493512 --- /dev/null +++ b/app/src/main/res/drawable/ic_twitter.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/shimmer_background.xml b/app/src/main/res/drawable/shimmer_background.xml new file mode 100644 index 000000000..12dd712e7 --- /dev/null +++ b/app/src/main/res/drawable/shimmer_background.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml index 702f6e875..188bc5368 100644 --- a/app/src/main/res/layout/activity_about.xml +++ b/app/src/main/res/layout/activity_about.xml @@ -207,12 +207,14 @@ android:layout_height="wrap_content" android:text="@string/contributors" /> - + app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" + app:spanCount="3" + android:nestedScrollingEnabled="false" /> diff --git a/app/src/main/res/layout/bottom_sheet_contributions.xml b/app/src/main/res/layout/bottom_sheet_contributions.xml new file mode 100644 index 000000000..839af767b --- /dev/null +++ b/app/src/main/res/layout/bottom_sheet_contributions.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/bottom_sheet_user_profile.xml b/app/src/main/res/layout/bottom_sheet_user_profile.xml new file mode 100644 index 000000000..9da594fa9 --- /dev/null +++ b/app/src/main/res/layout/bottom_sheet_user_profile.xml @@ -0,0 +1,249 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_contributor.xml b/app/src/main/res/layout/item_contributor.xml new file mode 100644 index 000000000..88b68bdd5 --- /dev/null +++ b/app/src/main/res/layout/item_contributor.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 471a1cdfb..057313d7a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -478,4 +478,6 @@ Coming soon! Enable Spy Tool Logs internal events to Xposed log for debugging. + Video Note Attachment + Add a "Video Note" button to the chat attachment menu to easily send instant videos. diff --git a/app/src/main/res/xml/preference_general_conversation.xml b/app/src/main/res/xml/preference_general_conversation.xml index f6d51b3b6..e14c6541d 100644 --- a/app/src/main/res/xml/preference_general_conversation.xml +++ b/app/src/main/res/xml/preference_general_conversation.xml @@ -119,6 +119,12 @@ app:summary="@string/show_contact_blocked_sum" app:title="@string/show_contact_blocked" /> + + \ No newline at end of file diff --git a/changelog.txt b/changelog.txt index dd3025af7..94c5c5314 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,20 +1,6 @@ [ADDED] -* Added Call Recording feature (Voice/Video as Audio) (Thanks to @mubashardev) -* Call Recording Toast Toggle: Added option to show/hide recording toast notifications (default: off) (Thanks to @mubashardev) -* [EXPERIMENTAL] Use "Force Restore Backup" option in Chat Backup settings to restore backups after login (Thanks to @mubashardev) -* Global Search: Added comprehensive search functionality across all app settings and features. (Thanks to @mubashardev) -* Recover deleted for me message: Added option to recover deleted for me messages. (Thanks to @mubashardev) -* Added Contributors section in About page to acknowledge the efforts of contributors. -* Added Support for WA/WB 2.26.7.xx +- Added video file as Video Note Attachment feature to chat attachment menu. + - Enable this in WaEnhancer: Settings > Conversation > Video Note Attachment [Fixed] -* Fixed profile picture download for old contacts -* Optimized initial app load -- Call recording filter (whitelist/blacklist) now works correctly -- Call recording on loudspeaker now captures both sides of the conversation -- Status download button now shows consistently -- Sharing someone else's status now correctly carries the media to the composer -- Anti-revoke now works in group chats - -Thanks @grkmcngns for detailed review and highlighting these issues. - +- Updated about section to include contributors along with their detailed contributions