Simplify disabling permissions request screen

- Move the preference from developer to general settings
- Add a "do not ask again" button (with a warning), but only when the
  permission check is triggered on startup
This commit is contained in:
José Rebelo
2025-08-04 21:35:13 +01:00
parent c4f2468de1
commit 742fccacec
9 changed files with 154 additions and 87 deletions
@@ -289,6 +289,7 @@ public class ControlCenterv2 extends AppCompatActivity
pesterWithPermissions = prefs.getBoolean("permission_pestering", true);
if (pesterWithPermissions && !PermissionsUtils.checkAllPermissions(this)) {
Intent permissionsIntent = new Intent(this, PermissionsActivity.class);
permissionsIntent.putExtra(PermissionsActivity.ARG_SHOW_DO_NOT_ASK_BUTTON, true);
startActivity(permissionsIntent);
}
}
@@ -25,14 +25,23 @@ import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.welcome.WelcomeFragmentPermissions;
public class PermissionsActivity extends AbstractGBActivity {
public static final String ARG_SHOW_DO_NOT_ASK_BUTTON = "show_do_not_ask";
@Override
protected void onCreate(Bundle savedInstanceState) {
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_permissions);
WelcomeFragmentPermissions permissionsFragment = new WelcomeFragmentPermissions();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
final WelcomeFragmentPermissions permissionsFragment = new WelcomeFragmentPermissions();
final Bundle args = new Bundle();
args.putBoolean(
WelcomeFragmentPermissions.ARG_SHOW_DO_NOT_ASK_BUTTON,
getIntent().getBooleanExtra(ARG_SHOW_DO_NOT_ASK_BUTTON, false)
);
permissionsFragment.setArguments(args);
final FragmentManager fragmentManager = getSupportFragmentManager();
final FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.fragment_container, permissionsFragment).commit();
}
}
@@ -18,61 +18,48 @@ package nodomain.freeyourgadget.gadgetbridge.activities.welcome;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.databinding.ActivityWelcomeBinding;
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
public class WelcomeActivity extends AbstractGBActivity {
private static final Logger LOG = LoggerFactory.getLogger(WelcomeActivity.class);
private ViewPager2 viewPager;
private WelcomeFragmentsPagerAdapter pagerAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
AbstractGBActivity.init(this, AbstractGBActivity.NO_ACTIONBAR);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_welcome);
final ActivityWelcomeBinding binding = ActivityWelcomeBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
if (getSupportActionBar() != null) {
getSupportActionBar().hide();
}
// Configure ViewPager2 with fragment adapter and default fragment
viewPager = findViewById(R.id.welcome_viewpager);
pagerAdapter = new WelcomeFragmentsPagerAdapter(this);
viewPager.setAdapter(pagerAdapter);
WelcomeFragmentsPagerAdapter pagerAdapter = new WelcomeFragmentsPagerAdapter(this);
binding.welcomeViewpager.setAdapter(pagerAdapter);
// Set up welcome page indicator
WelcomePageIndicator pageIndicator = findViewById(R.id.welcome_page_indicator);
pageIndicator.setViewPager(viewPager);
binding.welcomePageIndicator.setViewPager(binding.welcomeViewpager);
}
private class WelcomeFragmentsPagerAdapter extends FragmentStateAdapter {
private static class WelcomeFragmentsPagerAdapter extends FragmentStateAdapter {
public WelcomeFragmentsPagerAdapter(FragmentActivity fa) {
super(fa);
}
@NonNull
@Override
public Fragment createFragment(int position) {
switch (position) {
case 0:
return new WelcomeFragmentIntro();
case 1:
return new WelcomeFragmentOverview();
case 2:
return new WelcomeFragmentDocsSource();
case 3:
return new WelcomeFragmentPermissions();
default:
return new WelcomeFragmentGetStarted();
}
return switch (position) {
case 0 -> new WelcomeFragmentIntro();
case 1 -> new WelcomeFragmentOverview();
case 2 -> new WelcomeFragmentDocsSource();
case 3 -> new WelcomeFragmentPermissions();
default -> new WelcomeFragmentGetStarted();
};
}
@Override
@@ -27,38 +27,60 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.databinding.FragmentWelcomePermissionsBinding;
import nodomain.freeyourgadget.gadgetbridge.util.PermissionsUtils;
public class WelcomeFragmentPermissions extends Fragment {
private static final Logger LOG = LoggerFactory.getLogger(WelcomeFragmentPermissions.class);
public static final String ARG_SHOW_DO_NOT_ASK_BUTTON = "show_do_not_ask";
private RecyclerView permissionsListView;
private FragmentWelcomePermissionsBinding binding;
private PermissionAdapter permissionAdapter;
private Button requestAllButton;
private List<String> requestingPermissions = new ArrayList<>();
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_welcome_permissions, container, false);
binding = FragmentWelcomePermissionsBinding.inflate(getLayoutInflater(), container, false);
requestAllButton = view.findViewById(R.id.button_request_all);
requestAllButton.setOnClickListener(v -> {
final Bundle arguments = getArguments();
final boolean showDoNotAskAgain = arguments != null && arguments.getBoolean(ARG_SHOW_DO_NOT_ASK_BUTTON, false);
if (!showDoNotAskAgain) {
binding.buttonDoNotAskAgain.setVisibility(View.GONE);
}
binding.buttonDoNotAskAgain.setOnClickListener(v -> {
new MaterialAlertDialogBuilder(requireContext())
.setCancelable(true)
.setTitle(R.string.first_start_permissions_do_not_ask_again)
.setMessage(R.string.first_start_permissions_do_not_ask_warning_summary)
.setPositiveButton(R.string.ok, (dialog, which) -> {
GBApplication.getPrefs().getPreferences().edit()
.putBoolean("permission_pestering", false)
.apply();
requireActivity().finish();
})
.setNegativeButton(R.string.Cancel, (dialog, which) -> {
// do nothing
})
.show();
});
binding.buttonRequestAll.setOnClickListener(v -> {
List<PermissionsUtils.PermissionDetails> wantedPermissions = PermissionsUtils.getRequiredPermissionsList(requireActivity());
requestingPermissions = new ArrayList<>();
for (PermissionsUtils.PermissionDetails wantedPermission : wantedPermissions) {
@@ -67,20 +89,18 @@ public class WelcomeFragmentPermissions extends Fragment {
requestAllPermissions();
});
if (((AppCompatActivity)getActivity()).getSupportActionBar().isShowing()) {
final ActionBar supportActionBar = ((AppCompatActivity) requireActivity()).getSupportActionBar();
if (supportActionBar!= null && supportActionBar.isShowing()) {
// Hide title when the Action Bar is visible (i.e. when not in the first run flow)
view.findViewById(R.id.permissions_title).setVisibility(View.GONE);
binding.permissionsTitle.setVisibility(View.GONE);
}
// Initialize RecyclerView and data
permissionsListView = view.findViewById(R.id.permissions_list);
// Set up RecyclerView
permissionAdapter = new PermissionAdapter(PermissionsUtils.getRequiredPermissionsList(requireActivity()), requireContext());
permissionsListView.setLayoutManager(new LinearLayoutManager(requireContext()));
permissionsListView.setAdapter(permissionAdapter);
binding.permissionsList.setLayoutManager(new LinearLayoutManager(requireContext()));
binding.permissionsList.setAdapter(permissionAdapter);
return view;
return binding.getRoot();
}
@Override
@@ -88,7 +108,7 @@ public class WelcomeFragmentPermissions extends Fragment {
super.onResume();
permissionAdapter.notifyDataSetChanged();
if (PermissionsUtils.checkAllPermissions(requireActivity())) {
requestAllButton.setEnabled(false);
binding.buttonRequestAll.setEnabled(false);
}
if (!requestingPermissions.isEmpty()) {
requestAllPermissions();
@@ -114,7 +134,7 @@ public class WelcomeFragmentPermissions extends Fragment {
}
}
private class PermissionHolder extends RecyclerView.ViewHolder {
private static class PermissionHolder extends RecyclerView.ViewHolder {
TextView titleTextView;
TextView summaryTextView;
ImageView checkmarkImageView;
@@ -130,8 +150,8 @@ public class WelcomeFragmentPermissions extends Fragment {
}
private class PermissionAdapter extends RecyclerView.Adapter<PermissionHolder> {
private List<PermissionsUtils.PermissionDetails> permissionList;
private Context context;
private final List<PermissionsUtils.PermissionDetails> permissionList;
private final Context context;
public PermissionAdapter(List<PermissionsUtils.PermissionDetails> permissionList, Context context) {
this.permissionList = permissionList;
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M480,480ZM480,880q-139,-35 -229.5,-159.5T160,444v-244l320,-120 320,120v262q0,9 -1,19h-81q1,-10 1.5,-19t0.5,-18v-189l-240,-90 -240,90v189q0,121 68,220t172,132v84ZM680,880v-120L560,760v-80h120v-120h80v120h120v80L760,760v120h-80ZM420,600h120l-23,-129q20,-10 31.5,-29t11.5,-42q0,-33 -23.5,-56.5T480,320q-33,0 -56.5,23.5T400,400q0,23 11.5,42t31.5,29l-23,129Z" />
</vector>
+10
View File
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M480,880q-139,-35 -229.5,-159.5T160,444v-244l320,-120 320,120v244q0,152 -90.5,276.5T480,880ZM480,796q104,-33 172,-132t68,-220v-189l-240,-90 -240,90v189q0,121 68,220t172,132ZM480,480Z" />
</vector>
@@ -1,38 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:fillViewport="true"
android:gravity="center_horizontal"
android:orientation="vertical"
android:padding="8dp">
<TextView
android:id="@+id/permissions_title"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:textAlignment="center"
android:text="@string/first_start_permissions_title"
android:textSize="30sp"/>
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textSize="20sp"
android:textAlignment="center"
android:text="@string/first_start_permissions_desc" />
<TextView
android:id="@+id/permissions_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="@string/first_start_permissions_title"
android:textAlignment="center"
android:textSize="30sp" />
<Button
android:id="@+id/button_request_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/first_start_permissions_request_all_button"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/first_start_permissions_desc"
android:textAlignment="center"
android:textSize="20sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/permissions_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp" />
</LinearLayout>
<Button
android:id="@+id/button_do_not_ask_again"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/first_start_permissions_do_not_ask_again" />
<Button
android:id="@+id/button_request_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/first_start_permissions_request_all_button" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/permissions_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:nestedScrollingEnabled="false" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
+3
View File
@@ -2471,6 +2471,7 @@
<string name="device_unsupported">UNSUPPORTED</string>
<string name="device_experimental">EXPERIMENTAL</string>
<string name="error_background_service_reason">Starting the background service failed because of an exception - click here for more information.\n\nError:</string>
<string name="pref_check_permission_show_list">Show permission status</string>
<string name="pref_check_permission_status">Check permission status</string>
<string name="pref_check_permission_status_summary">Check and ask for missing permissions even when they might not be instantly needed. Disable this only if your devices actually doesn\'t support any of these features. Not granting a permission might cause issues!</string>
<string name="pref_show_changelog">Show changelog on startup</string>
@@ -3956,6 +3957,8 @@
<string name="first_start_permissions_title">Permissions</string>
<string name="first_start_permissions_desc">Gadgetbridge needs a lot of permissions to perform all its functions. Review the permissions and their purposes below.</string>
<string name="first_start_permissions_request_all_button">Request all permissions</string>
<string name="first_start_permissions_do_not_ask_again">Do not ask again</string>
<string name="first_start_permissions_do_not_ask_warning_summary">Not granting a permission might cause issues. You can open this screen again from the Settings screen.</string>
<string name="first_start_permissions_request_button">Request</string>
<string name="first_start_get_started_title">Get started</string>
<string name="first_start_get_started_desc">To get started, add your first device directly from this screen, restore a backup or start with a clean database.</string>
+17 -7
View File
@@ -20,6 +20,23 @@
android:title="@string/pref_title_general_autostartonboot"
android:icon="@drawable/ic_autoplay" />
<SwitchPreferenceCompat
android:defaultValue="true"
android:icon="@drawable/ic_shield"
android:key="permission_pestering"
android:layout="@layout/preference_checkbox"
android:summary="@string/pref_check_permission_status_summary"
android:title="@string/pref_check_permission_status" />
<Preference
android:title="@string/pref_check_permission_show_list"
android:key="permission_show_activity"
android:icon="@drawable/ic_encrypted_add">
<intent
android:targetPackage="@string/applicationId"
android:targetClass="nodomain.freeyourgadget.gadgetbridge.activities.PermissionsActivity" />
</Preference>
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="show_changelog"
@@ -385,13 +402,6 @@
android:summary="@string/pref_crash_notification_summary"
android:title="@string/pref_crash_notification_title"
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="permission_pestering"
android:layout="@layout/preference_checkbox"
android:summary="@string/pref_check_permission_status_summary"
android:title="@string/pref_check_permission_status"
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="cache_weather"