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); pesterWithPermissions = prefs.getBoolean("permission_pestering", true);
if (pesterWithPermissions && !PermissionsUtils.checkAllPermissions(this)) { if (pesterWithPermissions && !PermissionsUtils.checkAllPermissions(this)) {
Intent permissionsIntent = new Intent(this, PermissionsActivity.class); Intent permissionsIntent = new Intent(this, PermissionsActivity.class);
permissionsIntent.putExtra(PermissionsActivity.ARG_SHOW_DO_NOT_ASK_BUTTON, true);
startActivity(permissionsIntent); startActivity(permissionsIntent);
} }
} }
@@ -25,14 +25,23 @@ import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.welcome.WelcomeFragmentPermissions; import nodomain.freeyourgadget.gadgetbridge.activities.welcome.WelcomeFragmentPermissions;
public class PermissionsActivity extends AbstractGBActivity { public class PermissionsActivity extends AbstractGBActivity {
public static final String ARG_SHOW_DO_NOT_ASK_BUTTON = "show_do_not_ask";
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_permissions); setContentView(R.layout.activity_permissions);
WelcomeFragmentPermissions permissionsFragment = new WelcomeFragmentPermissions(); final WelcomeFragmentPermissions permissionsFragment = new WelcomeFragmentPermissions();
FragmentManager fragmentManager = getSupportFragmentManager(); final Bundle args = new Bundle();
FragmentTransaction transaction = fragmentManager.beginTransaction(); 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(); transaction.replace(R.id.fragment_container, permissionsFragment).commit();
} }
} }
@@ -18,61 +18,48 @@ package nodomain.freeyourgadget.gadgetbridge.activities.welcome;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter; import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
import org.slf4j.Logger; import nodomain.freeyourgadget.gadgetbridge.databinding.ActivityWelcomeBinding;
import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity; import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
public class WelcomeActivity extends AbstractGBActivity { public class WelcomeActivity extends AbstractGBActivity {
private static final Logger LOG = LoggerFactory.getLogger(WelcomeActivity.class);
private ViewPager2 viewPager;
private WelcomeFragmentsPagerAdapter pagerAdapter;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
AbstractGBActivity.init(this, AbstractGBActivity.NO_ACTIONBAR); AbstractGBActivity.init(this, AbstractGBActivity.NO_ACTIONBAR);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_welcome); final ActivityWelcomeBinding binding = ActivityWelcomeBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().hide(); getSupportActionBar().hide();
} }
// Configure ViewPager2 with fragment adapter and default fragment // Configure ViewPager2 with fragment adapter and default fragment
viewPager = findViewById(R.id.welcome_viewpager); WelcomeFragmentsPagerAdapter pagerAdapter = new WelcomeFragmentsPagerAdapter(this);
pagerAdapter = new WelcomeFragmentsPagerAdapter(this); binding.welcomeViewpager.setAdapter(pagerAdapter);
viewPager.setAdapter(pagerAdapter);
// Set up welcome page indicator // Set up welcome page indicator
WelcomePageIndicator pageIndicator = findViewById(R.id.welcome_page_indicator); binding.welcomePageIndicator.setViewPager(binding.welcomeViewpager);
pageIndicator.setViewPager(viewPager);
} }
private class WelcomeFragmentsPagerAdapter extends FragmentStateAdapter { private static class WelcomeFragmentsPagerAdapter extends FragmentStateAdapter {
public WelcomeFragmentsPagerAdapter(FragmentActivity fa) { public WelcomeFragmentsPagerAdapter(FragmentActivity fa) {
super(fa); super(fa);
} }
@NonNull
@Override @Override
public Fragment createFragment(int position) { public Fragment createFragment(int position) {
switch (position) { return switch (position) {
case 0: case 0 -> new WelcomeFragmentIntro();
return new WelcomeFragmentIntro(); case 1 -> new WelcomeFragmentOverview();
case 1: case 2 -> new WelcomeFragmentDocsSource();
return new WelcomeFragmentOverview(); case 3 -> new WelcomeFragmentPermissions();
case 2: default -> new WelcomeFragmentGetStarted();
return new WelcomeFragmentDocsSource(); };
case 3:
return new WelcomeFragmentPermissions();
default:
return new WelcomeFragmentGetStarted();
}
} }
@Override @Override
@@ -27,38 +27,60 @@ import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import org.slf4j.Logger; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.databinding.FragmentWelcomePermissionsBinding;
import nodomain.freeyourgadget.gadgetbridge.util.PermissionsUtils; import nodomain.freeyourgadget.gadgetbridge.util.PermissionsUtils;
public class WelcomeFragmentPermissions extends Fragment { 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 PermissionAdapter permissionAdapter;
private Button requestAllButton;
private List<String> requestingPermissions = new ArrayList<>(); private List<String> requestingPermissions = new ArrayList<>();
@Nullable @Nullable
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, 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); final Bundle arguments = getArguments();
requestAllButton.setOnClickListener(v -> { 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()); List<PermissionsUtils.PermissionDetails> wantedPermissions = PermissionsUtils.getRequiredPermissionsList(requireActivity());
requestingPermissions = new ArrayList<>(); requestingPermissions = new ArrayList<>();
for (PermissionsUtils.PermissionDetails wantedPermission : wantedPermissions) { for (PermissionsUtils.PermissionDetails wantedPermission : wantedPermissions) {
@@ -67,20 +89,18 @@ public class WelcomeFragmentPermissions extends Fragment {
requestAllPermissions(); 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) // 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 // Set up RecyclerView
permissionAdapter = new PermissionAdapter(PermissionsUtils.getRequiredPermissionsList(requireActivity()), requireContext()); permissionAdapter = new PermissionAdapter(PermissionsUtils.getRequiredPermissionsList(requireActivity()), requireContext());
permissionsListView.setLayoutManager(new LinearLayoutManager(requireContext())); binding.permissionsList.setLayoutManager(new LinearLayoutManager(requireContext()));
permissionsListView.setAdapter(permissionAdapter); binding.permissionsList.setAdapter(permissionAdapter);
return view; return binding.getRoot();
} }
@Override @Override
@@ -88,7 +108,7 @@ public class WelcomeFragmentPermissions extends Fragment {
super.onResume(); super.onResume();
permissionAdapter.notifyDataSetChanged(); permissionAdapter.notifyDataSetChanged();
if (PermissionsUtils.checkAllPermissions(requireActivity())) { if (PermissionsUtils.checkAllPermissions(requireActivity())) {
requestAllButton.setEnabled(false); binding.buttonRequestAll.setEnabled(false);
} }
if (!requestingPermissions.isEmpty()) { if (!requestingPermissions.isEmpty()) {
requestAllPermissions(); 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 titleTextView;
TextView summaryTextView; TextView summaryTextView;
ImageView checkmarkImageView; ImageView checkmarkImageView;
@@ -130,8 +150,8 @@ public class WelcomeFragmentPermissions extends Fragment {
} }
private class PermissionAdapter extends RecyclerView.Adapter<PermissionHolder> { private class PermissionAdapter extends RecyclerView.Adapter<PermissionHolder> {
private List<PermissionsUtils.PermissionDetails> permissionList; private final List<PermissionsUtils.PermissionDetails> permissionList;
private Context context; private final Context context;
public PermissionAdapter(List<PermissionsUtils.PermissionDetails> permissionList, Context context) { public PermissionAdapter(List<PermissionsUtils.PermissionDetails> permissionList, Context context) {
this.permissionList = permissionList; 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"?> <?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_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:fillViewport="true"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:orientation="vertical"
android:padding="8dp"> android:padding="8dp">
<TextView <LinearLayout
android:id="@+id/permissions_title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="30dp" android:gravity="center_horizontal"
android:textAlignment="center" android:orientation="vertical">
android:text="@string/first_start_permissions_title"
android:textSize="30sp"/>
<TextView <TextView
android:layout_width="match_parent" android:id="@+id/permissions_title"
android:layout_height="wrap_content" android:layout_width="match_parent"
android:layout_marginTop="20dp" android:layout_height="wrap_content"
android:textSize="20sp" android:layout_marginTop="30dp"
android:textAlignment="center" android:text="@string/first_start_permissions_title"
android:text="@string/first_start_permissions_desc" /> android:textAlignment="center"
android:textSize="30sp" />
<Button <TextView
android:id="@+id/button_request_all" android:layout_width="match_parent"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_height="wrap_content" android:layout_marginTop="20dp"
android:layout_marginTop="20dp" android:text="@string/first_start_permissions_desc"
android:text="@string/first_start_permissions_request_all_button"/> android:textAlignment="center"
android:textSize="20sp" />
<androidx.recyclerview.widget.RecyclerView <Button
android:id="@+id/permissions_list" android:id="@+id/button_do_not_ask_again"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="20dp" /> android:layout_marginTop="20dp"
</LinearLayout> 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_unsupported">UNSUPPORTED</string>
<string name="device_experimental">EXPERIMENTAL</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="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">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_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> <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_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_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_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_permissions_request_button">Request</string>
<string name="first_start_get_started_title">Get started</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> <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:title="@string/pref_title_general_autostartonboot"
android:icon="@drawable/ic_autoplay" /> 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 <SwitchPreferenceCompat
android:defaultValue="true" android:defaultValue="true"
android:key="show_changelog" android:key="show_changelog"
@@ -385,13 +402,6 @@
android:summary="@string/pref_crash_notification_summary" android:summary="@string/pref_crash_notification_summary"
android:title="@string/pref_crash_notification_title" android:title="@string/pref_crash_notification_title"
app:iconSpaceReserved="false" /> 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 <SwitchPreferenceCompat
android:defaultValue="true" android:defaultValue="true"
android:key="cache_weather" android:key="cache_weather"