Tim
Tim

Reputation: 707

Android runtime permissions on first startup

When I start my Android application for the first time, it will trigger a dialog to ask location permissions. The problem is that onRequestPermissionsResult is already being triggered before I had the chance to press allow or deny. Because of this, the application doesn't have the permissions on the first startup and the Geofences are not set. Of course, when I restart the application, all permissions are granted and the Geofences work like they should. How do I tackle this problem on the first startup?

public class MainActivity extends BaseActivity implements OnRetailerClickListener, NetworkListener, GoogleApiClient.ConnectionCallbacks {

    public final int REQUEST_LOCATION_PERMISSION = 2222;
    private GoogleApiClient googleApiClient;
    private ViewPager mViewPager;
    private PagerAdapter mPagerAdapter;
    private OnBackPressedListener onBackPressedListener;
    private List<Geofence> geofenceList;
    private Intent intent;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /* Setup custom toolbar for viewpager layout */
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        toolbar.setTitle("Overview");
        setSupportActionBar(toolbar);

        /* Setup TabLayout for viewpager tabs */
        TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
        tabLayout.addTab(tabLayout.newTab().setText(R.string.tab_products));
        tabLayout.addTab(tabLayout.newTab().setText(R.string.tab_map));
        tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);

        /* register to the geofence observer */
        AppClassWiring.geofenceDao().registerObserver(this);

        /* build up GoogleApiClient and connect, used to set the geofences */
        googleApiClient = new GoogleApiClient
                .Builder(this)
                .addConnectionCallbacks(this)
                .addApi(LocationServices.API).build();
        googleApiClient.connect();

        /* create a viewpager for the fragments */
        mViewPager = (ViewPager) findViewById(R.id.pager);
        mPagerAdapter = new PagerAdapter(getSupportFragmentManager(), tabLayout.getTabCount());
        mViewPager.setPageTransformer(true, new ZoomOutPageTransformer());
        mViewPager.setAdapter(mPagerAdapter);
        mViewPager.setOffscreenPageLimit(mPagerAdapter.getCount()); // all fragments are kept in memory because there are only 3
        initViewPagerListeners(mViewPager, tabLayout);
    }

    @Override
    protected void onResume() {
        super.onResume();
        if(!DeviceUtils.checkBluetooth())
            askBluetooth();
    }

    /**
     * The OnClickListener in the fragment will trigger this method.
     * This way the app can switch to the correct page in the viewpager
     *
     * @param retailer object received from fragment
     */
    @Override
    public void onRetailerClick(Retailer retailer) {
        mViewPager.setCurrentItem(PagerAdapter.FRAGMENT_MAP, true);
        mPagerAdapter.getItemForPosition(1).onRetailerClick(retailer);
    }

    /**
     * Init the ViewPagerListeners, used to add an onPageChangeLister for the TabLayout
     * also closes the keyboard when changing pages
     *
     * @param viewPager instance of the used viewpager
     * @param tabLayout instance of the used TabLayout above
     */
    private void initViewPagerListeners(final ViewPager viewPager, TabLayout tabLayout) {
        viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                DeviceUtils.hideKeyboard(MainActivity.this);
            }

            @Override
            public void onPageSelected(int position) {

            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
        tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                viewPager.setCurrentItem(tab.getPosition());
                DeviceUtils.hideKeyboard(MainActivity.this);
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {
            }
        });
    }

    /**
     * Builder method to build up a Geofencingrequest with the retrieved backend data
     *
     * @param geofences to provide the data, required for a geofence to be built. This can be:
     *                  an Id, Latitude, Longitude, Radius, ExpirationDuration and TransitionTypes
     * @return a new instance of a GeofencingRequest
     */
    private GeofencingRequest buildGeofenceRequest(List<Geofence> geofences) {
        GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
        builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
        builder.addGeofences(geofences);
        return builder.build();
    }

    @Override
    public void onConnectionSuspended(int i) {
        Toast.makeText(this, "Could not connect with the Google API", Toast.LENGTH_LONG).show();
    }

    private void askBluetooth() {
        new AlertDialog.Builder(this)
                .setTitle("Enable bluetooth")
                .setMessage("Bluetooth should be enabled, Turn bluetooth on?")
                .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        DeviceUtils.setBluetoothOn(true);
                    }
                })
                .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        DeviceUtils.setBluetoothOn(false);
                    }
                })
                .setIcon(android.R.drawable.stat_sys_data_bluetooth)
                .show();
    }

    /**
     * When the GoogleApiClient is connected,
     * the user is able to get all the geofences from the backend
     *
     * @param bundle null
     */
    @Override
    public void onConnected(@Nullable Bundle bundle) {
        /* Trigger the network call to get all geofences.
        The results can be shared with other classes that also listen to it */
        AppClassWiring.geofenceDao().getAllGeofences();
    }

    /**
     * Observable method that listens to the incoming results of the network call for geofences
     *
     * @param pointList recieved geofence/point objects from the API.
     *                  Used to build up a GeofencingRequest. After building the GeofencingRequest,
     *                  the data is provided to the GoogleApiClient to setup the geofences ready to trigger
     */
    @Override
    public void onResult(Object pointList) {
        List<Point> points = (List<Point>) pointList;
        geofenceList = new ArrayList<>();
        for (int i = 0; i < points.size(); i++) {
            geofenceList.add(new Geofence.Builder()
                    .setRequestId(points.get(i).getRetailer())
                    .setCircularRegion(
                            points.get(i).getValidLat(),
                            points.get(i).getValidLong(),
                            points.get(i).getValidRadius())
                    .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER
                            | Geofence.GEOFENCE_TRANSITION_EXIT)
                    .setExpirationDuration(Geofence.NEVER_EXPIRE)
                    .build());
        }
        intent = new Intent(this, GeoFencingService.class);
        // Permission check
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION_PERMISSION);
        } else {
            LocationServices.GeofencingApi.addGeofences(
                    googleApiClient,
                    buildGeofenceRequest(geofenceList),
                    PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
            );
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case REQUEST_LOCATION_PERMISSION: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                        LocationServices.GeofencingApi.addGeofences(
                                googleApiClient,
                                buildGeofenceRequest(geofenceList),
                                PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
                    }
                }
            }
        }
    }

    /**
     * @param error provides the error that was thrown by the retrofit call.
     */
    @Override
    public void onError(NetworkException error) {
        Toast.makeText(this, error.getMessage(), Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onDestroy() {
        onBackPressedListener = null;
        /* unregister from the geofence observer */
        AppClassWiring.geofenceDao().unregisterObserver(this);
        super.onDestroy();
    }

    @Override
    public void onBackPressed() {
        if (onBackPressedListener != null)
            onBackPressedListener.doBack();
        else
            super.onBackPressed();
    }

    public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
        this.onBackPressedListener = onBackPressedListener;
    }


}

Upvotes: 4

Views: 2654

Answers (2)

Vishal Vaishnav
Vishal Vaishnav

Reputation: 3422

Use this method in onCreate() method:

requestStoragePermission();

and define this method outside onCreate() method:

 private void requestStoragePermission() {
    if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
        return;
    if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
        return;
    if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED)
        return;
    if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED)
        return;


    ActivityCompat.requestPermissions(this, new String[]
            {
                    android.Manifest.permission.READ_EXTERNAL_STORAGE,
                    android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    android.Manifest.permission.CAMERA,
                    android.Manifest.permission.READ_PHONE_STATE,
            }, STORAGE_PERMISSION_CODE);
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

    //Checking the request code of our request
    if (requestCode == STORAGE_PERMISSION_CODE) {
        //If permission is granted
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            //Displaying a toast
        } else {
            //Displaying another toast if permission is not granted
            Toast.makeText(this, "Oops you just denied the permission", Toast.LENGTH_LONG).show();
        }
    }
}

Upvotes: 2

CommonsWare
CommonsWare

Reputation: 1006584

The problem is that onRequestPermissionsResult is already being triggered before I had the chance to press alow or deny.

The only way that will happen is if you have already granted the permission, and so the dialog does not appear. Or, if you decide to call onRequestPermissionsResult() yourself, which would be very strange.

Upvotes: 1

Related Questions