Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import com.nextcloud.talk.dagger.modules.ContextModule
import com.nextcloud.talk.dagger.modules.DatabaseModule
import com.nextcloud.talk.dagger.modules.RepositoryModule
import com.nextcloud.talk.dagger.modules.RestModule
import com.nextcloud.talk.dagger.modules.UtilsModule
import com.nextcloud.talk.dagger.modules.ViewModelModule
import com.nextcloud.talk.jobs.AccountRemovalWorker
import com.nextcloud.talk.jobs.CapabilitiesWorker
Expand Down Expand Up @@ -99,7 +100,8 @@ import javax.inject.Singleton
UserModule::class,
ArbitraryStorageModule::class,
ViewModelModule::class,
RepositoryModule::class
RepositoryModule::class,
UtilsModule::class
]
)
@Singleton
Expand Down
17 changes: 5 additions & 12 deletions app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_ID
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
import com.nextcloud.talk.utils.database.user.UserUtils
import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder
import com.nextcloud.talk.utils.text.Spans
import com.nextcloud.talk.webrtc.MagicWebSocketInstance
Expand Down Expand Up @@ -231,6 +232,9 @@ class ChatController(args: Bundle) :
@JvmField
var eventBus: EventBus? = null

@Inject
lateinit var permissionUtil: PlatformPermissionUtil

val disposableList = ArrayList<Disposable>()

var roomToken: String? = null
Expand Down Expand Up @@ -1111,17 +1115,6 @@ class ChatController(args: Bundle) :
}
}

private fun isCameraPermissionGranted(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return PermissionChecker.checkSelfPermission(
context!!,
Manifest.permission.CAMERA
) == PermissionChecker.PERMISSION_GRANTED
} else {
true
}
}

private fun startAudioRecording(file: String) {
binding.messageInputView.audioRecordDuration.base = SystemClock.elapsedRealtime()
binding.messageInputView.audioRecordDuration.start()
Expand Down Expand Up @@ -3128,7 +3121,7 @@ class ChatController(args: Bundle) :
}

fun sendPictureFromCamIntent() {
if (!isCameraPermissionGranted()) {
if (!permissionUtil.isCameraPermissionGranted()) {
requestCameraPermissions()
} else {
startActivityForResult(TakePhotoActivity.createIntent(context!!), REQUEST_CODE_PICK_CAMERA)
Expand Down
103 changes: 78 additions & 25 deletions app/src/main/java/com/nextcloud/talk/controllers/ProfileController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ package com.nextcloud.talk.controllers

import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.ColorStateList
import android.graphics.Bitmap
import android.graphics.BitmapFactory
Expand Down Expand Up @@ -52,6 +53,7 @@ import com.github.dhaval2404.imagepicker.ImagePicker.Companion.getError
import com.github.dhaval2404.imagepicker.ImagePicker.Companion.getFile
import com.github.dhaval2404.imagepicker.ImagePicker.Companion.with
import com.nextcloud.talk.R
import com.nextcloud.talk.activities.TakePhotoActivity
import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
Expand All @@ -76,13 +78,14 @@ import com.nextcloud.talk.utils.Mimetype.IMAGE_PREFIX
import com.nextcloud.talk.utils.Mimetype.IMAGE_PREFIX_GENERIC
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_MIME_TYPE_FILTER
import com.nextcloud.talk.utils.database.user.UserUtils
import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.ResponseBody
import retrofit2.Call
import retrofit2.Callback
Expand All @@ -91,10 +94,10 @@ import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.util.LinkedList
import java.util.Locale
import javax.inject.Inject

@AutoInjector(NextcloudTalkApplication::class)
@Suppress("Detekt.TooManyFunctions")
class ProfileController : NewBaseController(R.layout.controller_profile) {
private val binding: ControllerProfileBinding by viewBinding(ControllerProfileBinding::bind)

Expand All @@ -104,6 +107,9 @@ class ProfileController : NewBaseController(R.layout.controller_profile) {
@Inject
lateinit var userUtils: UserUtils

@Inject
lateinit var permissionUtil: PlatformPermissionUtil

private var currentUser: UserEntity? = null
private var edit = false
private var adapter: UserInfoAdapter? = null
Expand Down Expand Up @@ -197,6 +203,7 @@ class ProfileController : NewBaseController(R.layout.controller_profile) {
val credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token)
binding.avatarUpload.setOnClickListener { sendSelectLocalFileIntent() }
binding.avatarChoose.setOnClickListener { showBrowserScreen() }
binding.avatarCamera.setOnClickListener { checkPermissionAndTakePicture() }
binding.avatarDelete.setOnClickListener {
ncApi.deleteAvatar(
credentials,
Expand Down Expand Up @@ -493,7 +500,19 @@ class ProfileController : NewBaseController(R.layout.controller_profile) {
startActivityForResult(avatarIntent, REQUEST_CODE_SELECT_REMOTE_FILES)
}

fun handleAvatar(remotePath: String?) {
private fun checkPermissionAndTakePicture() {
if (permissionUtil.isCameraPermissionGranted()) {
takePictureForAvatar()
} else {
requestPermissions(arrayOf(android.Manifest.permission.CAMERA), REQUEST_PERMISSION_CAMERA)
}
}

private fun takePictureForAvatar() {
startActivityForResult(TakePhotoActivity.createIntent(context), REQUEST_CODE_TAKE_PICTURE)
}

private fun handleAvatar(remotePath: String?) {
val uri = currentUser!!.baseUrl + "/index.php/apps/files/api/v1/thumbnail/512/512/" +
Uri.encode(remotePath, "/")
val downloadCall = ncApi.downloadResizedImage(
Expand All @@ -511,30 +530,54 @@ class ProfileController : NewBaseController(R.layout.controller_profile) {
})
}

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_PERMISSION_CAMERA) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
takePictureForAvatar()
} else {
Toast
.makeText(context, context.getString(R.string.take_photo_permission), Toast.LENGTH_LONG)
.show()
}
}
}

// only possible with API26
private fun saveBitmapAndPassToImagePicker(bitmap: Bitmap) {
var file: File? = null
val file: File = saveBitmapToTempFile(bitmap) ?: return
openImageWithPicker(file)
}

private fun saveBitmapToTempFile(bitmap: Bitmap): File? {
try {
FileUtils.removeTempCacheFile(
this.context!!,
AVATAR_PATH
)
file = FileUtils.getTempCacheFile(
this.context!!,
AVATAR_PATH
)
val file = createTempFileForAvatar()
try {
FileOutputStream(file).use { out -> bitmap.compress(Bitmap.CompressFormat.PNG, FULL_QUALITY, out) }
FileOutputStream(file).use { out ->
bitmap.compress(Bitmap.CompressFormat.PNG, FULL_QUALITY, out)
}
return file
} catch (e: IOException) {
Log.e(TAG, "Error compressing bitmap", e)
}
} catch (e: IOException) {
Log.e(TAG, "Error creating temporary avatar image", e)
}
if (file == null) {
// TODO exception
return
}
return null
}

private fun createTempFileForAvatar(): File? {
FileUtils.removeTempCacheFile(
this.context,
AVATAR_PATH
)
return FileUtils.getTempCacheFile(
context,
AVATAR_PATH
)
}

private fun openImageWithPicker(file: File) {
val intent = with(activity!!)
.fileOnly()
.crop()
Expand All @@ -546,20 +589,24 @@ class ProfileController : NewBaseController(R.layout.controller_profile) {
startActivityForResult(intent, REQUEST_CODE_IMAGE_PICKER)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK) {
if (requestCode == REQUEST_CODE_IMAGE_PICKER) {
uploadAvatar(getFile(intent))
uploadAvatar(getFile(data))
} else if (requestCode == REQUEST_CODE_SELECT_REMOTE_FILES) {
val pathList = intent?.getStringArrayListExtra(RemoteFileBrowserActivity.EXTRA_SELECTED_PATHS)
val pathList = data?.getStringArrayListExtra(RemoteFileBrowserActivity.EXTRA_SELECTED_PATHS)
if (pathList?.size!! >= 1) {
handleAvatar(pathList[0])
}
} else if (requestCode == REQUEST_CODE_TAKE_PICTURE) {
data?.data?.path?.let {
openImageWithPicker(File(it))
}
} else {
Log.w(TAG, "Unknown intent request code")
}
} else if (resultCode == ImagePicker.RESULT_ERROR) {
Toast.makeText(activity, getError(intent), Toast.LENGTH_SHORT).show()
Toast.makeText(activity, getError(data), Toast.LENGTH_SHORT).show()
} else {
Log.i(TAG, "Task Cancelled")
}
Expand All @@ -570,11 +617,11 @@ class ProfileController : NewBaseController(R.layout.controller_profile) {
builder.setType(MultipartBody.FORM)
builder.addFormDataPart(
"files[]", file!!.name,
RequestBody.create(IMAGE_PREFIX_GENERIC.toMediaTypeOrNull(), file)
file.asRequestBody(IMAGE_PREFIX_GENERIC.toMediaTypeOrNull())
)
val filePart: MultipartBody.Part = MultipartBody.Part.createFormData(
"files[]", file.name,
RequestBody.create(IMAGE_JPG.toMediaTypeOrNull(), file)
file.asRequestBody(IMAGE_JPG.toMediaTypeOrNull())
)

// upload file
Expand All @@ -595,7 +642,11 @@ class ProfileController : NewBaseController(R.layout.controller_profile) {
}

override fun onError(e: Throwable) {
Toast.makeText(applicationContext, "Error", Toast.LENGTH_LONG).show()
Toast.makeText(
applicationContext, context.getString(R.string.default_error_msg),
Toast
.LENGTH_LONG
).show()
Log.e(TAG, "Error uploading avatar", e)
}

Expand Down Expand Up @@ -706,7 +757,7 @@ class ProfileController : NewBaseController(R.layout.controller_profile) {
)
}
if (controller.edit &&
controller.editableFields.contains(item.field.toString().toLowerCase(Locale.ROOT))
controller.editableFields.contains(item.field.toString().lowercase())
) {
holder.binding.userInfoEditText.isEnabled = true
holder.binding.userInfoEditText.isFocusableInTouchMode = true
Expand Down Expand Up @@ -823,6 +874,8 @@ class ProfileController : NewBaseController(R.layout.controller_profile) {
private const val DEFAULT_RETRIES: Long = 3
private const val MAX_SIZE: Int = 1024
private const val REQUEST_CODE_IMAGE_PICKER: Int = 1
private const val REQUEST_CODE_TAKE_PICTURE: Int = 2
private const val REQUEST_PERMISSION_CAMERA: Int = 1
private const val FULL_QUALITY: Int = 100
private const val HIGH_EMPHASIS_ALPHA: Float = 0.87f
private const val MEDIUM_EMPHASIS_ALPHA: Float = 0.6f
Expand Down
38 changes: 38 additions & 0 deletions app/src/main/java/com/nextcloud/talk/dagger/modules/UtilsModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Nextcloud Talk application
*
* @author Álvaro Brey
* Copyright (C) 2022 Álvaro Brey
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.nextcloud.talk.dagger.modules

import android.content.Context
import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
import com.nextcloud.talk.utils.permissions.PlatformPermissionUtilImpl
import dagger.Module
import dagger.Provides
import dagger.Reusable

@Module(includes = [ContextModule::class])
class UtilsModule {
@Provides
@Reusable
fun providePermissionUtil(context: Context): PlatformPermissionUtil {
return PlatformPermissionUtilImpl(context)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Nextcloud Talk application
*
* @author Álvaro Brey
* Copyright (C) 2022 Álvaro Brey
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.nextcloud.talk.utils.permissions

interface PlatformPermissionUtil {
fun isCameraPermissionGranted(): Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Nextcloud Talk application
*
* @author Álvaro Brey
* Copyright (C) 2022 Álvaro Brey
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.nextcloud.talk.utils.permissions

import android.Manifest
import android.content.Context
import android.os.Build
import androidx.core.content.PermissionChecker

class PlatformPermissionUtilImpl(private val context: Context) : PlatformPermissionUtil {

override fun isCameraPermissionGranted(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return PermissionChecker.checkSelfPermission(
context,
Manifest.permission.CAMERA
) == PermissionChecker.PERMISSION_GRANTED
} else {
true
}
}
}
Loading