Problem using FileProvider and ActivityResultContracts.TakePicture() - android

Hello I am trying to use the new AndroidX Activity Result APIs, especially the ActivityResultContracts.TakePicture() contract.
I created a sample project to test before putting in my real application.
MainActivity
class MainActivity : AppCompatActivity() {
private var _binding: ActivityMainBinding?=null
private val binding get() =_binding!!
private var photoUri:Uri?=null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
_binding = ActivityMainBinding.inflate(layoutInflater)
setupButtons()
setContentView(binding.root)
}
private val takePicture = registerForActivityResult(ActivityResultContracts.TakePicture()) { isSaved ->
if (isSaved) {
binding.imageView.setImageURI(photoUri)
}
}
private fun setupButtons() {
binding.button.setOnClickListener {
photoUri = pickUri()
takePicture.launch(photoUri)
}
}
private fun pickUri(): Uri {
val path = applicationContext.getExternalFilesDir(null)?.absolutePath
val picturesFolder = File(path, "pictures")
Log.d(TAG, "pickUri: ${picturesFolder.absolutePath}")
val sdf = SimpleDateFormat("yyyyMMdd_HHmmss")
val dt = sdf.format(Date())
val fileName = "profile-picture-user-4-${dt}.jpg"
val imagePath = File(applicationContext.filesDir, "pictures")
val newFile = File(imagePath, fileName)
if (!picturesFolder.exists()){
picturesFolder.mkdirs()
Log.d(TAG, "pickUri: directoryCreated")
}
return getUriForFile(applicationContext, "com.example.resultactivitytutorial.fileprovider", newFile)
}
What I am trying to achieve is: If not created, create a folder named pictures inside PACKAGE_NAME-> files to save a profile picture, and save the photo shot inside this folder using the new ActivityResultApi.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.resultactivitytutorial">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.ResultActivityTutorial">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.resultactivitytutorial.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
</application>
</manifest>
And the #xml/file_paths file is:
<paths>
<files-path
name="pictures"
path="pictures/" />
</paths>
The Problem is I am getting the error:
W/ImageView: Unable to open content: content://com.example.resultactivitytutorial.fileprovider/pictures/profile-picture-user-4-20210118_132245.jpg
java.io.FileNotFoundException: open failed: ENOENT (No such file or directory)
I need some help with fixing this problem. Thanks in advance!!

Related

can't read a text file from an external storage through Kotlin

I am making a app to read a text file from an external storage through Kotlin. Even after adding permission tags and changing options in the manifest file, the following error is displayed and the text file cannot be imported. How can I solve this?
errCode:
java.lang.IllegalArgumentException: File content://com.android.externalstorage.documents/document/primary%3ADocuments%2Ftest.txt contains a path separator
kotlin File
class MainActivity : AppCompatActivity() {
val getTextCode:Int = 2
override fun onCreate(savedInstanceState: Bundle?) {
val vBinding = ActivityMainBinding.inflate(layoutInflater)
super.onCreate(savedInstanceState)
setContentView(vBinding.root)
vBinding.getFromTxt.setOnClickListener {
val downUri = Uri.parse("/storage/self/primary/Documents")
openFile(downUri)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == getTextCode && resultCode == Activity.RESULT_OK) {
try{
data?.data?.also { uri ->
Toast.makeText(this, uri.toString(), Toast.LENGTH_SHORT).show()
Log.d("test", uri.toString())
openFileInput(uri.toString()).bufferedReader().useLines{ lines->
Log.d("test", lines.toString())
}
}
}catch(err:Exception){
Log.d("test", err.toString())
}
}
}
private fun openFile(pickerInitialUri: Uri){
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "text/*"
putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
}
startActivityForResult(intent, getTextCode)
}
}
manifest File
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.questionbank">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.QuestionBank"
android:requestLegacyExternalStorage="true"
>
<activity
android:name=".EditActivity"
android:exported="false" />
<activity
android:name=".TestActivity"
android:exported="false" />
<activity
android:name=".PrintActivity"
android:exported="false" />
<activity
android:name=".InputActivity"
android:exported="false" />
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
openFileInput(uri.toString()
The method openFileInput() only expects a file name. Not a file path or a complete content scheme. And it would search for a file with that name in getFilesDir().
If you want to read the file using the uri then open an inputstream for the uri and read from the stream. In Java:
InputStream is = getContentResolver().openInputStream(uri);
Further you do not need any permission to do so. Nor do you have to change options whatever you mean by that.

Send Card Details to NFC Terminal

I am trying to create payment app which support the tap & pay functionality. I am already done with the Connecting App to NFC terminal with use of SELECT APDU Command.
Now, I want to send the stored card details from app to NFC Terminal, so it can make transaction.
Can you suggest me any kind of documents, which can help me to achieve it, such as how to send the details securely and in what kind of format it should be send?
any help would be appreciated.
here is the code
Android Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.nfcemulator">
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc.hce"
android:required="true" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.NFCEmulator">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".service.HCEService"
android:exported="true"
android:permission="android.permission.BIND_NFC_SERVICE">
<intent-filter>
<action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE" />
</intent-filter>
<meta-data
android:name="android.nfc.cardemulation.host_apdu_service"
android:resource="#xml/apduservice" />
</service>
</application>
</manifest>
HCE Service
class HCEService: HostApduService() {
companion object {
val TAG = "Host Card Emulator"
val STATUS_SUCCESS = "9000"
val STATUS_FAILED = "6F00"
val CLA_NOT_SUPPORTED = "6E00"
val INS_NOT_SUPPORTED = "6D00"
val AID = "A0000002471001"
val SELECT_INS = "A4"
val DEFAULT_CLA = "00"
val MIN_APDU_LENGTH = 12
}
override fun onDeactivated(reason: Int) {
Log.d(TAG, "Deactivated: " + reason)
}
override fun processCommandApdu(commandApdu: ByteArray?, extras: Bundle?): ByteArray {
if (commandApdu == null) {
return Utils.hexStringToByteArray(STATUS_FAILED)
}
val hexCommandApdu = Utils.toHex(commandApdu)
if (hexCommandApdu.length < MIN_APDU_LENGTH) {
return Utils.hexStringToByteArray(STATUS_FAILED)
}
if (hexCommandApdu.substring(0, 2) != DEFAULT_CLA) {
return Utils.hexStringToByteArray(CLA_NOT_SUPPORTED)
}
if (hexCommandApdu.substring(2, 4) != SELECT_INS) {
return Utils.hexStringToByteArray(INS_NOT_SUPPORTED)
}
if (hexCommandApdu.substring(10, 24) == AID) {
return Utils.hexStringToByteArray(STATUS_SUCCESS)
} else {
return Utils.hexStringToByteArray(STATUS_FAILED)
}
}
}
apduservices.xml
<?xml version="1.0" encoding="utf-8"?>
<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="#string/hce_service"
android:requireDeviceUnlock="false">
<aid-group android:description="#string/aid_groups"
android:category="other">
<aid-filter android:name="325041592E5359532E4444463031"/>
</aid-group>
</host-apdu-service>

Media playing commands on Google Assistant are not launching my app

I’m having some trouble getting Google Assistant to play media for my media app.
I have verified using the Media Controller Tester app that the play actions are working. I am able to use Open Feature Actions with Assistant.
But every time I try to use phrases like Play AppName or Play Station on AppName, Assistant tries to launch TuneIn.
If I try Play music on AppName Assistant launches YouTube Music .
I have tried everything in the docs here and have used UAMP as a base (of which I am also seeing similar behaviour)
Here is a cut down version of my audio service:
class AudioService : MediaBrowserServiceCompat() {
#Inject
lateinit var audioServiceBrowserManager: AudioServiceBrowserManager
#Inject
lateinit var schedulerProvider: RxSchedulerProvider
#Inject
lateinit var playbackPreparer: AppPlaybackPreparer
#Inject
lateinit var playbackControlDispatcher: AppControlDispatcher
#Inject
lateinit var audioProvider: AudioProvider
#Inject
lateinit var playbackManager: PlaybackManager
#Inject
lateinit var mediaSessionChangedCallback: MediaSessionChangedCallback
private lateinit var mediaSession: MediaSessionCompat
private lateinit var mediaSessionConnector: MediaSessionConnector
private lateinit var mediaController: MediaControllerCompat
private lateinit var audioNotificationManager: AudioNotificationManager
private lateinit var packageValidator: PackageValidator
private val disposables = CompositeDisposable()
companion object {
private const val SEEK_BACKWARD_INCREMENT = 15000
private const val SEEK_FORWARD_INCREMENT = 30000
private const val MEDIA_SESSION_TAG: String = "AudioService"
internal const val METADATA_MEDIA_TYPE = "au.net.app.player.service.metadata.mediaType"
internal const val METADATA_MEDIA_TYPE_ONDEMAND_VIDEO = 0L
internal const val METADATA_MEDIA_TYPE_ONDEMAND_AUDIO = 2L
internal const val METADATA_MEDIA_TYPE_LIVE = 1L
val DEFAULT_PLAYBACK_STATE: PlaybackStateCompat = PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_NONE, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f)
.build()
}
override fun onCreate() {
AndroidInjection.inject(this)
super.onCreate()
mediaSession = MediaSessionCompat(this, MEDIA_SESSION_TAG).apply {
setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)
isActive = true
}
sessionToken = mediaSession.sessionToken
mediaSessionConnector = MediaSessionConnector(mediaSession).apply {
setRewindIncrementMs(SEEK_BACKWARD_INCREMENT)
setFastForwardIncrementMs(SEEK_FORWARD_INCREMENT)
setPlaybackPreparer(playbackPreparer)
setQueueNavigator(AppQueueNavigator(mediaSession, audioProvider, this#AudioService, this))
setControlDispatcher(playbackControlDispatcher)
setPlayer(playbackManager.currentPlayback.playerImpl)
registerCustomCommandReceiver(playbackManager.mediaSessionCommandReceiver)
}
disposables.add(
playbackManager.currentPlaybackObservable.subscribe { currentPlayback ->
mediaSessionConnector.setPlayer(currentPlayback.playerImpl)
}
)
mediaController = MediaControllerCompat(this, mediaSession)
mediaController.registerCallback(mediaSessionChangedCallback)
try {
audioNotificationManager = AudioNotificationManager(this, mediaController)
} catch (e: RemoteException) {
throw IllegalStateException("Could not create a MediaNotificationManager", e)
}
packageValidator = PackageValidator(this, R.xml.allowed_media_browser_callers)
}
private var currentLoadChildrenDisposable: Disposable? = null
override fun onLoadChildren(parentId: String, result: Result<MutableList<MediaBrowserCompat.MediaItem>>) {
Timber.d("""
onLoadChildren(
parentId = $parentId,
result = $result
)
""".trimIndent())
}
override fun onGetRoot(clientPackageName: String, clientUid: Int, rootHints: Bundle?): BrowserRoot? {
Timber.d("""
onGetRoot(
clientPackageName = $clientPackageName,
clientUid = $clientUid,
rootHints = $rootHints
)
""".trimIndent())
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
return START_STICKY
}
override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
playbackManager.handleStop()
stopSelf()
}
override fun onDestroy() {
super.onDestroy()
playbackManager.handleStop()
disposables.dispose()
mediaSession.isActive = false
mediaSession.release()
}
}
The module manifest (note - the service is not in my main app module)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="au.net.app.player.service">
<uses-permission android:name="android.permission.INTERNET" />
<application>
<service
android:name="au.net.app.player.service.AudioService"
android:exported="true">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
<receiver android:name="androidx.media.session.MediaButtonReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
<meta-data
android:name="com.google.android.gms.car.application"
android:resource="#xml/automotive_app_desc" />
</application>
</manifest>
In the main app manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="au.net.app"
android:installLocation="auto">
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
android:name=".AppApplication"
android:allowBackup="true"
android:label="#string/app_name"
android:icon="#mipmap/ic_launcher"
android:networkSecurityConfig="#xml/network_security_config"
android:supportsRtl="true"
android:hardwareAccelerated="true"
android:theme="#style/AppTheme">
<activity
android:name=".mainscreen.MainActivity"
android:launchMode="singleTask"
android:label="#string/app_name"
android:theme="#style/AppTheme.NoActionBar"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- Intent filters to open Feature screens -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="feature"
android:pathPattern="/.*"
android:scheme="${APP_SCHEME}" />
</intent-filter>
<!-- Declares that the app handles SEARCH intent for media playback -->
<!-- This is mandatory for Android Auto support: -->
<!-- https://stackoverflow.com/questions/31953155/android-auto-voice-cannot-perform-play-x-on-y/31976075#31976075 -->
<intent-filter>
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<!-- Required for Google Assistant integration -->
<meta-data android:name="com.google.android.actions" android:resource="#xml/actions" />
</application>
</manifest>
I have also tried setting up my playback state with:
val DEFAULT_PLAYBACK_STATE: PlaybackStateCompat = PlaybackStateCompat.Builder()
.setActions(getSupportedActions())
.setState(PlaybackStateCompat.STATE_NONE, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f)
.build()
private fun getSupportedActions(): Long {
return PlaybackStateCompat.ACTION_PLAY or
PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH or
PlaybackStateCompat.ACTION_SKIP_TO_NEXT or
PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS or
PlaybackStateCompat.ACTION_PLAY_PAUSE
}
But my understanding is I shouldn't need to as MediaSessionConnector should take care of that (since I am using ExoPlayer). Adding this does not help.

Failed to find configured root that contains when Intent is in fragment

There are many questions and answers about this topic on the internet but unfortunately it didnt solve my problem.
I get a error every time I try to intent a Camera for a picture of video recording within my Fragment.
The path where I want to save those files are in /data/data/com.example.testing/files/*
The error that I always get is: Failed to find configured root that "contains/data/data/com.example.testing/files/test.jpg" and "Failed to find configured root that contains/data/data/com.example.testing/files/test.mp4"
The complete error log:
2020-12-16 00:14:32.093 6473-6473/com.example.testing E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.testing, PID: 6473
java.lang.IllegalArgumentException: Failed to find configured root that contains /data/data/com.example.testing/files/test.jpg
at androidx.core.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:744)
at androidx.core.content.FileProvider.getUriForFile(FileProvider.java:418)
at com.example.testing.fragments.InvitesCheck.capturePhoto(InvitesCheck.kt:141)
at com.example.testing.fragments.InvitesCheck.access$capturePhoto(InvitesCheck.kt:32)
at com.example.testing.fragments.InvitesCheck$onViewCreated$1.onClick(InvitesCheck.kt:53)
at android.view.View.performClick(View.java:7192)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:992)
at android.view.View.performClickInternal(View.java:7166)
at android.view.View.access$3500(View.java:824)
at android.view.View$PerformClick.run(View.java:27592)
at android.os.Handler.handleCallback(Handler.java:888)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:213)
at android.app.ActivityThread.main(ActivityThread.java:8178)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1101)
I tried different setups but unfortunately no luck.
This is my Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.testing">
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera" />
<application
android:allowBackup="false"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.Testing">
<activity
android:name=".MainActivity"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
</application>
</manifest>
I am using the following functions to call the camera:
private fun capturePhoto() {
val file = File(requireContext().filesDir.path, "test.jpg")
val uri = FileProvider.getUriForFile(requireActivity(), BuildConfig.APPLICATION_ID.toString() + ".provider", file)
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri)
startActivityForResult(intent, REQUEST_CODE)
}
private fun captureVideo() {
val file = File(requireContext().filesDir.path, "test.mp4")
val uri = FileProvider.getUriForFile(requireActivity(), BuildConfig.APPLICATION_ID.toString() + ".provider", file)
val intent = Intent(MediaStore.ACTION_VIDEO_CAPTURE)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 10);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri)
startActivityForResult(intent, REQUEST_VIDEO)
}
xml/provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="external_files" path="."/>
</paths>
What am I doing wrong?
Thank you in advance.
Reading up on this question, it seems that you should be using Context.getExternalFilesDir() instead of Context.getFilesDir()` whenever you're creating the File to which to write to.
This would end up looking something like this:
private fun capturePhoto() {
val file = File(requireContext().getExternalFilesDir(null).path, "test.jpg")
val uri = FileProvider.getUriForFile(requireActivity(), BuildConfig.APPLICATION_ID.toString() + ".provider", file)
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri)
startActivityForResult(intent, REQUEST_CODE)
}
BONUS: I would recommend you take a look at the MediaStore class if you're planning on making a media-oriented app. Here's an official intro for it.

Crash on GoogleSignInClient.signInIntent - A required meta-data tag in your app's AndroidManifest.xml does not exist

I use Google OAuth to let users sign in to the android app via Google account. When the user taps the Google login button it immediately crashes with information that
A required meta-data tag in your app's AndroidManifest.xml does not exist. You must have the following declaration within the <application> element: <meta-data android:name="com.google.android.gms.version" android:value="#integer/google_play_services_version" />
doesn't exists in AndroidManifest when it does. google_play_services_versions value is 12451000, hardcoding it doesn't changes anything
Stacktrace
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.mateuszsiedlecki.whatsthere, PID: 578
java.lang.IllegalStateException: A required meta-data tag in your app's AndroidManifest.xml does not exist. You must have the following declaration within the <application> element: <meta-data android:name="com.google.android.gms.version" android:value="#integer/google_play_services_version" />
at com.google.android.gms.common.GooglePlayServicesUtilLight.isGooglePlayServicesAvailable(com.google.android.gms:play-services-basement##17.1.1:17)
at com.google.android.gms.common.GoogleApiAvailabilityLight.isGooglePlayServicesAvailable(com.google.android.gms:play-services-basement##17.1.1:5)
at com.google.android.gms.common.GoogleApiAvailability.isGooglePlayServicesAvailable(com.google.android.gms:play-services-base##17.1.0:96)
at com.google.android.gms.auth.api.signin.GoogleSignInClient.zzg(com.google.android.gms:play-services-auth##18.0.0:9)
at com.google.android.gms.auth.api.signin.GoogleSignInClient.getSignInIntent(com.google.android.gms:play-services-auth##18.0.0:18)
at com.mateuszsiedlecki.whatsthere.login.SocialLoginFragment.googleSignIn(SocialLoginFragment.kt:101)
at com.mateuszsiedlecki.whatsthere.login.SocialLoginFragment.access$googleSignIn(SocialLoginFragment.kt:24)
at com.mateuszsiedlecki.whatsthere.login.SocialLoginFragment$onViewCreated$2.onClick(SocialLoginFragment.kt:70)
at android.view.View.performClick(View.java:7161)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:967)
at android.view.View.performClickInternal(View.java:7133)
at android.view.View.access$3500(View.java:804)
at android.view.View$PerformClick.run(View.java:27416)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:241)
at android.app.ActivityThread.main(ActivityThread.java:7604)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:941)
Code
class SocialLoginFragment : Fragment() {
val gso: GoogleSignInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.requestProfile()
.requestId()
.build()
private val facebookRequestedData = "id,first_name,last_name,email,picture,name"
private var fbCallbackManager: CallbackManager = CallbackManager.Factory.create()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_social_login, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
FacebookSdk.sdkInitialize(this.context)
LoginManager.getInstance().registerCallback(
fbCallbackManager,
object : FacebookCallback<LoginResult?> {
override fun onSuccess(loginResult: LoginResult?) {
Log.d("login", "Facebook Login")
val fbUser = getFbUserData(loginResult!!.accessToken)
UserDataManager.user = fbUser
loginFinished()
}
override fun onCancel() {
Log.d("login", "Facebook Login Cancel")
}
override fun onError(exception: FacebookException) {
Log.d("login", "Facebook Login error")
}
}
)
val googleLoginButton = getView()?.findViewById<Button>(R.id.googleLogin)
googleLoginButton?.setOnClickListener {
googleSignIn()
}
val facebookLoginButton = getView()?.findViewById<Button>(R.id.facebookLogin)
facebookLoginButton?.setOnClickListener {
LoginManager.getInstance().logInWithReadPermissions(
this,
listOf("public_profile", "email")
)
}
val emailLoginButton = getView()?.findViewById<Button>(R.id.emailLogin)
emailLoginButton?.setOnClickListener {
val intent = Intent(this.context, EmailLoginActivity::class.java)
startActivity(intent)
}
val noLogin: Button = getView()?.findViewById<Button>(R.id.noLogin) as Button
noLogin.setOnClickListener {
(activity as LoginActivity?)?.openEmailFragment()
}
}
private fun googleSignIn() {
val mGoogleSignInClient: GoogleSignInClient = GoogleSignIn.getClient(
this.context!!,
gso
)
val signInIntent: Intent = mGoogleSignInClient.signInIntent // crashing here
startActivityForResult(signInIntent, 1)
}
...
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mateuszsiedlecki.whatsthere">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="false"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<meta-data
android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
<activity android:name=".EmailLoginActivity" />
<activity
android:name=".osmMapsActivity"
android:label="#string/title_activity_maps" />
<activity android:name=".login.LoginActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="com.facebook.sdk.ApplicationId"
android:value="#string/facebook_app_id" />
<activity
android:name="com.facebook.FacebookActivity"
android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
android:label="#string/app_name" />
<activity
android:name="com.facebook.CustomTabActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="#string/fb_login_protocol_scheme" />
</intent-filter>
</activity>
</application>
</manifest>

Categories

Resources