Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[android] checkPermissions always return 'denied' #129

Open
steadev opened this issue Sep 14, 2024 · 9 comments
Open

[android] checkPermissions always return 'denied' #129

steadev opened this issue Sep 14, 2024 · 9 comments

Comments

@steadev
Copy link

steadev commented Sep 14, 2024

version: 6.0.6

Describe the bug
A clear and concise description of what the bug is.

checkPermissions always return 'denied', even though permission granted.

requestPermissions returns 'granted'..

and for checkPermissions, ios works fine

@hamzamac
Copy link

hamzamac commented Oct 2, 2024

I also have a problem on my app using the very same package version, it crashes in production when attempting to access contacts. I my case it crashes before asking user for permission.
The Debug version works fine though.
I have not investigated it might be it crashes for the same reasons as yours.
iOS works fine also for me.

@hamzamac
Copy link

hamzamac commented Oct 2, 2024

I see there is a new version 6.1.1 I will test that.

@hamzamac
Copy link

hamzamac commented Oct 2, 2024

Upgrading to version 6.1.1 did not fix my problem

@hamzamac
Copy link

hamzamac commented Oct 2, 2024

the problem
still exists

@shaman79
Copy link

I have exactly the same problem. On Android the app does not even try to open permission dialog.
Strange thing is that even when I completely remove my app and install it again, it does not even ask for permissions and automatically fails as denied. In the app details on Android I see that the permission is denied.
If anyone knows how to fix this it would be highly appreciated.

@shaman79
Copy link

Can someone from the dev team please let us know if this is going to be fixed? Thanks.

@tafelnl
Copy link
Member

tafelnl commented Nov 22, 2024

We're using the helpers provided by the Capacitor code itself. We don't do any logic on this. Are you sure you added the necessary lines in AndroidManifest.xml? Could you please share a reproduction

@shaman79
Copy link

Yes, I added the permissions to the manifest file, checked everything 20 times. It does not make any sense. Maybe the helpers are not working ok in later versions of Android. I am going to check that too.

Below is the complete service class we are using.

import axios from 'axios'
import responseInterceptor from './response.interceptor'
import authInterceptor from './auth.interceptor'
import {Contacts} from '@capacitor-community/contacts'
import {storage} from './storage'
import {generateAvatarInitials} from '../utils/generateAvatarInitials'
import {store} from '../store'
const API_URL = import.meta.env.VITE_API_URL

class ContactService {
    api

    constructor() {
        this.api = axios.create({baseURL: API_URL})
        authInterceptor(this.api)
        responseInterceptor(this.api)
    }

    async fetchContactsFromPhone(term) {
        try {
            const perms = await Contacts.getPermissions()

            if (perms.granted || perms.contacts === 'granted') {
                console.debug('ContactService.fetchContactsFromPhone: Permissions granted')
                const {contacts} = await Contacts.getContacts()
                console.debug('ContactService.fetchContactsFromPhone: fetched contacts:')
                console.debug(contacts)
                term = term.toLowerCase()

                return contacts
                    .map(this.mapLocalContactToContact)
                    .filter(c => c.fullname?.length)
                    .filter(c => {
                        if (!term?.length) {
                            return true
                        }

                        return c.fullname?.toLowerCase().includes(term) ||
                            c.phone?.toLowerCase().includes(term) ||
                            c.phone?.replace(/ /g, '').toLowerCase().includes(term) ||
                            c.email?.toLowerCase().includes(term)
                    })
            } else {
                console.warn('ContactService.fetchContactsFromPhone: Permissions not granted')
                console.warn(perms)
            }
        } catch (e) {
            if (e.code === 'UNIMPLEMENTED') {
                console.error('Cannot fetch local contacts from the device.')
            } else {
                console.error(e)
            }
        }

        console.warn('ContactService.fetchContactsFromPhone: Returning empty array')
        return []
    }

    async fetchListCached() {
        return await storage.getJSON('contacts') || []
    }

    async fetchList(filter, term, page, options) {
        let url = `contacts?page=${page}&filter=${encodeURIComponent(JSON.stringify(filter))}`
        url = options?.limit ? url += `&limit=${options.limit}` : url
        url = options?.sort ? url += `&sort=${options.sort}` : url
        url = options?.sortDir ? url += `&sortDir=${options.sortDir}` : url

        let res

        try {
            const response = await this.api.get(url)
            res = response.data
            res.items = res.items
                .map(i => {
                    if (!i.fullname?.trim().length) {
                        i.fullname = i.phone || i.email
                    }
                    return i
                })
                .filter(i => i.fullname?.length)
        } catch (e) {
            if (e.response?.status === 401) {
                store.commit('logout')
            }
            console.error(e)
            //return []
        }

        const localContacts = await this.fetchContactsFromPhone(term)
        // console.log('local contacts')
        // console.log(localContacts)

        res.items = [...localContacts, ...res.items]
            .map(i => {
                i.avatar = generateAvatarInitials(i.fullname || i.email)
                i.sort = i.firstname || i.surname || i.fullname || i.phone || i.email
                return i
            })
            .filter(i => i.fullname?.length)
            .sort((a, b) => a.sort?.toString().localeCompare(b.sort))

        storage.setJSON('contacts', res.items)

        return res
    }

    async fetchItem(id, isLocal) {
        let contact
        if (isLocal) {
            const {contacts} = await Contacts.getContacts()
            contact = contacts.find(i => i.contactId === id)
            if (contact) {
                return this.mapLocalContactToContact(contact)
            }
        } else {
            contact = await  this.api.get(`contacts/${id}`).then(response => response.data)
        }

        contact.avatar = generateAvatarInitials(contact.fullname || contact.email)
        return contact
    }

    mapLocalContactToContact(i) {
        const firstPhoneNumber = i.phoneNumbers?.length ? i.phoneNumbers[0].number : null
        const firstEmail = i.emails?.length ? i.emails[0].address : null
        return {
            _id: i.contactId,
            local: true,
            fullname: i.displayName || firstPhoneNumber || firstEmail || null,
            phone: firstPhoneNumber,
            email: firstEmail,
            company: i.company || null
        }
    }

    async createNewContact(data) {
        const {data: res} = await this.api.post('/contacts', data)
        return res
    }

    async updateContact(id, data) {
        const {data: res} = await this.api.put(`contacts/${id}`, data)
        return res
    }
    async deleteContact(id) {
        return await this.api.delete(`contacts/${id}`)
    }
}

export default new ContactService()

@tafelnl
Copy link
Member

tafelnl commented Nov 26, 2024

Without a reproduction we won't be able to help you unfortunately

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants