Skip to content

Commit

Permalink
[Debloater] Display suggestions in the details dialog
Browse files Browse the repository at this point in the history
https://github.com/MuntashirAkon/android-debloat-list#criteria-for-suggestions

Signed-off-by: Muntashir Al-Islam <muntashirakon@riseup.net>
  • Loading branch information
MuntashirAkon committed Jul 12, 2023
1 parent f0f070a commit a30a47b
Show file tree
Hide file tree
Showing 13 changed files with 594 additions and 200 deletions.
1 change: 1 addition & 0 deletions app/src/main/assets/suggestions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"id":"org.bromite.bromite","label":"Bromite","source":"f","repo":"https:\/\/github.com\/bromite\/bromite","_id":"browsers"},{"id":"org.chromium.chrome","label":"Vanadium","repo":"https:\/\/github.com\/GrapheneOS\/Vanadium","reason":"GrapheneOS only","_id":"browsers"},{"id":"itkach.aard2","label":"Aard 2","reason":"Simple yet powerful dictionary reader that support multiple dictionaries with offline access.\nSLOB files https:\/\/github.com\/itkach\/slob\/wiki\/Dictionaries","source":"fg","repo":"https:\/\/github.com\/itkach\/aard2-android","_id":"dictionaries"},{"id":"eu.faircode.email","label":"FairEmail","source":"fg","repo":"https:\/\/github.com\/M66B\/FairEmail","_id":"email_clients"},{"id":"com.fsck.k9","label":"K-9 Mail","source":"fg","repo":"https:\/\/github.com\/thundernest\/k-9","_id":"email_clients"},{"id":"me.zhanghai.android.files","label":"Material Files","source":"fg","repo":"https:\/\/github.com\/zhanghai\/MaterialFiles","_id":"file_managers"},{"id":"deckers.thibault.aves.libre","label":"Aves Libre","source":"f","repo":"https:\/\/github.com\/deckerst\/aves","_id":"gallery"},{"id":"com.simplemobiletools.gallery.pro","label":"Simple Gallery Pro","source":"f","repo":"https:\/\/github.com\/SimpleMobileTools\/Simple-Gallery","_id":"gallery"},{"id":"org.smc.inputmethod.indic","label":"Indic Keyboard","source":"fg","repo":"https:\/\/gitlab.com\/indicproject\/Indic-Keyboard","_id":"keyboards"},{"id":"dev.patrickgold.florisboard","label":"FlorisBoard","source":"f","repo":"https:\/\/github.com\/florisboard\/florisboard","_id":"keyboards"},{"id":"com.x8bit.bitwarden","label":"Bitwarden","reason":"Regular security audits https:\/\/bitwarden.com\/help\/is-bitwarden-audited\/#third-party-security-audits.\nRecommended to download from their official F-Droid repo https:\/\/mobileapp.bitwarden.com\/fdroid\/.","source":"g","repo":"https:\/\/github.com\/bitwarden\/mobile","_id":"password_managers"},{"id":"com.moez.QKSMS","label":"QKSMS","source":"fg","repo":"https:\/\/github.com\/moezbhatti\/qksms","_id":"sms"},{"id":"org.bromite.webview","label":"Bromite SystemWebView","source":"f","repo":"https:\/\/github.com\/bromite\/bromite","_id":"webviews"}]
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
// SPDX-License-Identifier: GPL-3.0-or-later

package io.github.muntashirakon.AppManager.debloat;

import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.SpannableStringBuilder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.widget.LinearLayoutCompat;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;

import com.google.android.material.button.MaterialButton;
import com.google.android.material.chip.Chip;
import com.google.android.material.textview.MaterialTextView;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import io.github.muntashirakon.AppManager.R;
import io.github.muntashirakon.AppManager.details.AppDetailsActivity;
import io.github.muntashirakon.AppManager.utils.UIUtils;
import io.github.muntashirakon.AppManager.utils.appearance.ColorCodes;
import io.github.muntashirakon.dialog.CapsuleBottomSheetDialogFragment;
import io.github.muntashirakon.util.UiUtils;
import io.github.muntashirakon.widget.FlowLayout;
import io.github.muntashirakon.widget.MaterialAlertView;
import io.github.muntashirakon.widget.RecyclerView;


public class BloatwareDetailsDialog extends CapsuleBottomSheetDialogFragment {
public static final String TAG = BloatwareDetailsDialog.class.getSimpleName();

public static final String ARG_PACKAGE_NAME = "pkg";

@NonNull
public static BloatwareDetailsDialog getInstance(@NonNull String packageName) {
BloatwareDetailsDialog fragment = new BloatwareDetailsDialog();
Bundle args = new Bundle();
args.putString(ARG_PACKAGE_NAME, packageName);
fragment.setArguments(args);
return fragment;
}

private ImageView mAppIconView;
private MaterialButton mOpenAppInfoButton;
private TextView mAppLabelView;
private TextView mPackageNameView;
private FlowLayout mFlowLayout;
private MaterialAlertView mWarningView;
private MaterialTextView mDescriptionView;
private LinearLayoutCompat mSuggestionContainer;
private RecyclerView mSuggestionView;
private SuggestionsAdapter mAdapter;


@NonNull
@Override
public View initRootView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.dialog_bloatware_details, container, false);
}

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
String packageName = requireArguments().getString(ARG_PACKAGE_NAME);
if (packageName == null) {
dismiss();
return;
}
DebloaterViewModel viewModel = new ViewModelProvider(requireActivity()).get(DebloaterViewModel.class);
mAppIconView = view.findViewById(R.id.icon);
mOpenAppInfoButton = view.findViewById(R.id.info);
mAppLabelView = view.findViewById(R.id.name);
mPackageNameView = view.findViewById(R.id.package_name);
mFlowLayout = view.findViewById(R.id.tag_cloud);
mWarningView = view.findViewById(R.id.alert_text);
mDescriptionView = view.findViewById(R.id.apk_description);
mSuggestionContainer = view.findViewById(R.id.container);
mSuggestionView = view.findViewById(R.id.recycler_view);
mSuggestionView.setLayoutManager(new LinearLayoutManager(requireContext()));
mAdapter = new SuggestionsAdapter();
mSuggestionView.setAdapter(mAdapter);

viewModel.getDebloatObjectLiveData().observe(getViewLifecycleOwner(), debloatObject -> {
if (debloatObject == null) {
dismiss();
return;
}
updateDialog(debloatObject);
updateDialog(debloatObject.getSuggestions());
});
viewModel.findDebloatObject(packageName);
}

private void updateDialog(@NonNull DebloatObject debloatObject) {
Drawable icon = debloatObject.getIcon();
mAppIconView.setImageDrawable(icon != null ? icon : requireActivity().getPackageManager().getDefaultActivityIcon());
int[] users = debloatObject.getUsers();
if (users != null && users.length > 0) {
mOpenAppInfoButton.setOnClickListener(v -> {
Intent appDetailsIntent = AppDetailsActivity.getIntent(requireContext(), debloatObject.packageName,
users[0]);
startActivity(appDetailsIntent);
dismiss();
});
} else {
mOpenAppInfoButton.setVisibility(View.GONE);
}
CharSequence label = debloatObject.getLabel();
mAppLabelView.setText(label != null ? label : debloatObject.packageName);
mPackageNameView.setText(debloatObject.packageName);
String warning = debloatObject.getWarning();
if (warning != null) {
mWarningView.setText(warning);
if (debloatObject.getRemoval() != DebloatObject.REMOVAL_CAUTION) {
mWarningView.setAlertType(MaterialAlertView.ALERT_TYPE_INFO);
}
} else mWarningView.setVisibility(View.GONE);
mDescriptionView.setText(getDescription(debloatObject.getDescription(), debloatObject.getWebRefs()));
// Add tags
int removalColor;
@StringRes
int removalRes;
switch (debloatObject.getRemoval()) {
case DebloatObject.REMOVAL_SAFE:
removalColor = ColorCodes.getRemovalSafeIndicatorColor(requireContext());
removalRes = R.string.debloat_removal_safe_short_description;
break;
default:
case DebloatObject.REMOVAL_CAUTION:
removalColor = ColorCodes.getRemovalCautionIndicatorColor(requireContext());
removalRes = R.string.debloat_removal_caution_short_description;
break;
case DebloatObject.REMOVAL_REPLACE:
removalColor = ColorCodes.getRemovalReplaceIndicatorColor(requireContext());
removalRes = R.string.debloat_removal_replace_short_description;
break;
}
mFlowLayout.removeAllViews();
addTag(mFlowLayout, debloatObject.type);
addTag(mFlowLayout, removalRes, removalColor);
}

private void updateDialog(@Nullable List<SuggestionObject> suggestionObjects) {
if (suggestionObjects == null || suggestionObjects.isEmpty()) {
mSuggestionContainer.setVisibility(View.GONE);
return;
}
mSuggestionContainer.setVisibility(View.VISIBLE);
mAdapter.setList(suggestionObjects);
}

@NonNull
private CharSequence getDescription(@NonNull String description, @Nullable String[] refSites) {
SpannableStringBuilder sb = new SpannableStringBuilder();
sb.append(description.trim());
if (refSites == null || refSites.length == 0) {
return sb;
}
// Add references
return sb.append(UIUtils.getBoldString("\n\nReferences\n"))
.append(UiUtils.getOrderedList(Arrays.asList(refSites)));
}

private void addTag(@NonNull ViewGroup parent, @StringRes int titleRes, @ColorInt int textColor) {
Chip chip = (Chip) LayoutInflater.from(requireContext()).inflate(R.layout.item_chip, parent, false);
chip.setText(titleRes);
if (textColor >= 0) {
chip.setTextColor(textColor);
}
parent.addView(chip);
}

private void addTag(@NonNull ViewGroup parent, @NonNull CharSequence title) {
Chip chip = (Chip) LayoutInflater.from(requireContext()).inflate(R.layout.item_chip, parent, false);
chip.setText(title);
parent.addView(chip);
}

private class SuggestionsAdapter extends RecyclerView.Adapter<SuggestionsAdapter.SuggestionViewHolder> {
private final List<SuggestionObject> mSuggestions = Collections.synchronizedList(new ArrayList<>());

public SuggestionsAdapter() {
}

public void setList(@NonNull List<SuggestionObject> suggestions) {
mSuggestions.clear();
mSuggestions.addAll(suggestions);
notifyDataSetChanged();
}

@NonNull
@Override
public SuggestionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_bloatware_details, parent, false);
return new SuggestionViewHolder(v);
}

@Override
public void onBindViewHolder(@NonNull SuggestionViewHolder holder, int position) {
SuggestionObject suggestion = mSuggestions.get(position);
holder.labelView.setText(suggestion.getLabel());
holder.packageNameView.setText(suggestion.packageName);
int[] users = suggestion.getUsers();
if (users != null && users.length > 0) {
MaterialButton appInfoButton = holder.marketOrAppInfoButton;
appInfoButton.setIconResource(io.github.muntashirakon.ui.R.drawable.ic_information);
appInfoButton.setOnClickListener(v -> {
Intent appDetailsIntent = AppDetailsActivity.getIntent(requireContext(), suggestion.packageName,
users[0]);
startActivity(appDetailsIntent);
});
} else {
MaterialButton marketButton = holder.marketOrAppInfoButton;
marketButton.setIconResource(suggestion.isInFDroidMarket() ? R.drawable.ic_frost_fdroid : R.drawable.ic_frost_aurorastore);
marketButton.setOnClickListener(v -> {
Intent appDetailsIntent = suggestion.getMarketLink();
appDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
startActivity(appDetailsIntent);
} catch (Throwable th) {
UIUtils.displayLongToast("Error: " + th.getMessage());
}
});
}
String reason = suggestion.getReason();
StringBuilder sb = new StringBuilder();
if (reason != null) sb.append(reason).append("\n");
sb.append(suggestion.getRepo());
holder.repoView.setText(sb);
}

@Override
public int getItemCount() {
return mSuggestions.size();
}

@Override
public long getItemId(int position) {
return mSuggestions.get(position).hashCode();
}

private class SuggestionViewHolder extends RecyclerView.ViewHolder {
final TextView labelView;
final TextView packageNameView;
final TextView repoView;
final MaterialButton marketOrAppInfoButton;

public SuggestionViewHolder(@NonNull View itemView) {
super(itemView);
labelView = itemView.findViewById(R.id.name);
packageNameView = itemView.findViewById(R.id.package_name);
repoView = itemView.findViewById(R.id.message);
marketOrAppInfoButton = itemView.findViewById(R.id.info);
}
}
}
}
Loading

0 comments on commit a30a47b

Please sign in to comment.