Media: grab some code from Riot legacy
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.riotredesign.core.dialogs
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import im.vector.riotredesign.R
|
||||||
|
|
||||||
|
internal abstract class DialogAdapter(context: Context) : ArrayAdapter<DialogListItem>(context, R.layout.item_dialog) {
|
||||||
|
|
||||||
|
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
|
||||||
|
var view = convertView
|
||||||
|
if (view == null) {
|
||||||
|
view = LayoutInflater.from(context).inflate(R.layout.item_dialog, parent, false)
|
||||||
|
view.tag = DialogListItemHolder(view)
|
||||||
|
}
|
||||||
|
(view!!.tag as DialogListItemHolder).let {
|
||||||
|
it.icon.setImageResource(getItem(position).iconRes)
|
||||||
|
it.text.setText(getItem(position).titleRes)
|
||||||
|
}
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.riotredesign.core.dialogs
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import im.vector.riotredesign.core.dialogs.DialogAdapter
|
||||||
|
import im.vector.riotredesign.core.dialogs.DialogListItem
|
||||||
|
|
||||||
|
internal class DialogCallAdapter(context: Context) : DialogAdapter(context) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
add(DialogListItem.StartVoiceCall)
|
||||||
|
add(DialogListItem.StartVideoCall)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.riotredesign.core.dialogs
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import im.vector.riotredesign.R
|
||||||
|
|
||||||
|
internal sealed class DialogListItem(@DrawableRes val iconRes: Int,
|
||||||
|
@StringRes val titleRes: Int) {
|
||||||
|
|
||||||
|
object StartVoiceCall : DialogListItem(R.drawable.voice_call_green, R.string.action_voice_call)
|
||||||
|
object StartVideoCall : DialogListItem(R.drawable.video_call_green, R.string.action_video_call)
|
||||||
|
|
||||||
|
object SendFile : DialogListItem(R.drawable.ic_material_file, R.string.option_send_files)
|
||||||
|
object SendVoice : DialogListItem(R.drawable.vector_micro_green, R.string.option_send_voice)
|
||||||
|
object SendSticker : DialogListItem(R.drawable.ic_send_sticker, R.string.option_send_sticker)
|
||||||
|
object TakePhoto : DialogListItem(R.drawable.ic_material_camera, R.string.option_take_photo)
|
||||||
|
object TakeVideo : DialogListItem(R.drawable.ic_material_videocam, R.string.option_take_video)
|
||||||
|
object TakePhotoVideo : DialogListItem(R.drawable.ic_material_camera, R.string.option_take_photo_video)
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.riotredesign.core.dialogs
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import butterknife.BindView
|
||||||
|
import butterknife.ButterKnife
|
||||||
|
import im.vector.riotredesign.R
|
||||||
|
|
||||||
|
class DialogListItemHolder(view: View) {
|
||||||
|
|
||||||
|
@BindView(R.id.adapter_item_dialog_icon)
|
||||||
|
lateinit var icon: ImageView
|
||||||
|
|
||||||
|
@BindView(R.id.adapter_item_dialog_text)
|
||||||
|
lateinit var text: TextView
|
||||||
|
|
||||||
|
init {
|
||||||
|
ButterKnife.bind(this, view)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.riotredesign.core.dialogs
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import im.vector.riotredesign.core.platform.Restorable
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
|
private const val KEY_DIALOG_IS_DISPLAYED = "DialogLocker.KEY_DIALOG_IS_DISPLAYED"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to avoid displaying twice the same dialog
|
||||||
|
*/
|
||||||
|
class DialogLocker() : Restorable {
|
||||||
|
|
||||||
|
private var isDialogDisplayed: Boolean = false
|
||||||
|
|
||||||
|
private fun unlock() {
|
||||||
|
isDialogDisplayed = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun lock() {
|
||||||
|
isDialogDisplayed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun displayDialog(builder: () -> AlertDialog.Builder): AlertDialog? {
|
||||||
|
return if (isDialogDisplayed) {
|
||||||
|
Timber.w("Filtered dialog request")
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
builder
|
||||||
|
.invoke()
|
||||||
|
.create()
|
||||||
|
.apply {
|
||||||
|
setOnShowListener { lock() }
|
||||||
|
setOnCancelListener { unlock() }
|
||||||
|
setOnDismissListener { unlock() }
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
outState.putBoolean(KEY_DIALOG_IS_DISPLAYED, isDialogDisplayed)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
|
||||||
|
isDialogDisplayed = savedInstanceState?.getBoolean(KEY_DIALOG_IS_DISPLAYED, false) == true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.riotredesign.core.dialogs
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import im.vector.riotredesign.core.dialogs.DialogAdapter
|
||||||
|
import im.vector.riotredesign.core.dialogs.DialogListItem
|
||||||
|
|
||||||
|
internal class DialogSendItemAdapter(context: Context, items: MutableList<DialogListItem>) : DialogAdapter(context) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
addAll(items)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.riotredesign.core.dialogs
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.text.TextWatcher
|
||||||
|
import android.widget.Button
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import com.google.android.material.textfield.TextInputEditText
|
||||||
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
|
import im.vector.riotredesign.R
|
||||||
|
|
||||||
|
class ExportKeysDialog {
|
||||||
|
|
||||||
|
fun show(activity: Activity, exportKeyDialogListener: ExportKeyDialogListener) {
|
||||||
|
val dialogLayout = activity.layoutInflater.inflate(R.layout.dialog_export_e2e_keys, null)
|
||||||
|
val builder = AlertDialog.Builder(activity)
|
||||||
|
.setTitle(R.string.encryption_export_room_keys)
|
||||||
|
.setView(dialogLayout)
|
||||||
|
|
||||||
|
val passPhrase1EditText = dialogLayout.findViewById<TextInputEditText>(R.id.dialog_e2e_keys_passphrase_edit_text)
|
||||||
|
val passPhrase2EditText = dialogLayout.findViewById<TextInputEditText>(R.id.dialog_e2e_keys_confirm_passphrase_edit_text)
|
||||||
|
val passPhrase2Til = dialogLayout.findViewById<TextInputLayout>(R.id.dialog_e2e_keys_confirm_passphrase_til)
|
||||||
|
val exportButton = dialogLayout.findViewById<Button>(R.id.dialog_e2e_keys_export_button)
|
||||||
|
val textWatcher = object : TextWatcher {
|
||||||
|
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun afterTextChanged(s: Editable) {
|
||||||
|
when {
|
||||||
|
TextUtils.isEmpty(passPhrase1EditText.text) -> {
|
||||||
|
exportButton.isEnabled = false
|
||||||
|
passPhrase2Til.error = null
|
||||||
|
}
|
||||||
|
TextUtils.equals(passPhrase1EditText.text, passPhrase2EditText.text) -> {
|
||||||
|
exportButton.isEnabled = true
|
||||||
|
passPhrase2Til.error = null
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
exportButton.isEnabled = false
|
||||||
|
passPhrase2Til.error = activity.getString(R.string.passphrase_passphrase_does_not_match)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
passPhrase1EditText.addTextChangedListener(textWatcher)
|
||||||
|
passPhrase2EditText.addTextChangedListener(textWatcher)
|
||||||
|
|
||||||
|
val exportDialog = builder.show()
|
||||||
|
|
||||||
|
exportButton.setOnClickListener {
|
||||||
|
exportKeyDialogListener.onPassphrase(passPhrase1EditText.text.toString())
|
||||||
|
|
||||||
|
exportDialog.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface ExportKeyDialogListener {
|
||||||
|
fun onPassphrase(passphrase: String)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,397 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.riotredesign.core.utils
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Build
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import im.vector.riotredesign.R
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
private const val LOG_TAG = "PermissionUtils"
|
||||||
|
|
||||||
|
// Android M permission request code management
|
||||||
|
private const val PERMISSIONS_GRANTED = true
|
||||||
|
private const val PERMISSIONS_DENIED = !PERMISSIONS_GRANTED
|
||||||
|
|
||||||
|
// Permission bit
|
||||||
|
private const val PERMISSION_BYPASSED = 0x0
|
||||||
|
const val PERMISSION_CAMERA = 0x1
|
||||||
|
private const val PERMISSION_WRITE_EXTERNAL_STORAGE = 0x1 shl 1
|
||||||
|
private const val PERMISSION_RECORD_AUDIO = 0x1 shl 2
|
||||||
|
private const val PERMISSION_READ_CONTACTS = 0x1 shl 3
|
||||||
|
|
||||||
|
// Permissions sets
|
||||||
|
const val PERMISSIONS_FOR_AUDIO_IP_CALL = PERMISSION_RECORD_AUDIO
|
||||||
|
const val PERMISSIONS_FOR_VIDEO_IP_CALL = PERMISSION_CAMERA or PERMISSION_RECORD_AUDIO
|
||||||
|
const val PERMISSIONS_FOR_TAKING_PHOTO = PERMISSION_CAMERA or PERMISSION_WRITE_EXTERNAL_STORAGE
|
||||||
|
const val PERMISSIONS_FOR_MEMBERS_SEARCH = PERMISSION_READ_CONTACTS
|
||||||
|
const val PERMISSIONS_FOR_MEMBER_DETAILS = PERMISSION_READ_CONTACTS
|
||||||
|
const val PERMISSIONS_FOR_ROOM_AVATAR = PERMISSION_CAMERA
|
||||||
|
const val PERMISSIONS_FOR_VIDEO_RECORDING = PERMISSION_CAMERA or PERMISSION_RECORD_AUDIO
|
||||||
|
const val PERMISSIONS_FOR_WRITING_FILES = PERMISSION_WRITE_EXTERNAL_STORAGE
|
||||||
|
|
||||||
|
private const val PERMISSIONS_EMPTY = PERMISSION_BYPASSED
|
||||||
|
|
||||||
|
// Request code to ask permission to the system (arbitrary values)
|
||||||
|
const val PERMISSION_REQUEST_CODE = 567
|
||||||
|
const val PERMISSION_REQUEST_CODE_LAUNCH_CAMERA = 568
|
||||||
|
const val PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_CAMERA = 569
|
||||||
|
const val PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_VIDEO_CAMERA = 570
|
||||||
|
const val PERMISSION_REQUEST_CODE_AUDIO_CALL = 571
|
||||||
|
const val PERMISSION_REQUEST_CODE_VIDEO_CALL = 572
|
||||||
|
const val PERMISSION_REQUEST_CODE_EXPORT_KEYS = 573
|
||||||
|
const val PERMISSION_REQUEST_CODE_CHANGE_AVATAR = 574
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log the used permissions statuses.
|
||||||
|
*/
|
||||||
|
fun logPermissionStatuses(context: Context) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
val permissions = Arrays.asList(
|
||||||
|
Manifest.permission.CAMERA,
|
||||||
|
Manifest.permission.RECORD_AUDIO,
|
||||||
|
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||||
|
Manifest.permission.READ_CONTACTS)
|
||||||
|
|
||||||
|
Timber.d("## logPermissionStatuses() : log the permissions status used by the app")
|
||||||
|
|
||||||
|
for (permission in permissions) {
|
||||||
|
Timber.d(("Status of [$permission] : " +
|
||||||
|
if (PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(context, permission))
|
||||||
|
"PERMISSION_GRANTED"
|
||||||
|
else
|
||||||
|
"PERMISSION_DENIED"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See [.checkPermissions]
|
||||||
|
*
|
||||||
|
* @param permissionsToBeGrantedBitMap
|
||||||
|
* @param activity
|
||||||
|
* @return true if the permissions are granted (synchronous flow), false otherwise (asynchronous flow)
|
||||||
|
*/
|
||||||
|
fun checkPermissions(permissionsToBeGrantedBitMap: Int,
|
||||||
|
activity: Activity,
|
||||||
|
requestCode: Int = PERMISSION_REQUEST_CODE): Boolean {
|
||||||
|
return checkPermissions(permissionsToBeGrantedBitMap, activity, null, requestCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See [.checkPermissions]
|
||||||
|
*
|
||||||
|
* @param permissionsToBeGrantedBitMap
|
||||||
|
* @param fragment
|
||||||
|
* @return true if the permissions are granted (synchronous flow), false otherwise (asynchronous flow)
|
||||||
|
*/
|
||||||
|
fun checkPermissions(permissionsToBeGrantedBitMap: Int,
|
||||||
|
fragment: Fragment,
|
||||||
|
requestCode: Int = PERMISSION_REQUEST_CODE): Boolean {
|
||||||
|
return checkPermissions(permissionsToBeGrantedBitMap, fragment.activity, fragment, requestCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the permissions provided in the list are granted.
|
||||||
|
* This is an asynchronous method if permissions are requested, the final response
|
||||||
|
* is provided in onRequestPermissionsResult(). In this case checkPermissions()
|
||||||
|
* returns false.
|
||||||
|
* <br></br>If checkPermissions() returns true, the permissions were already granted.
|
||||||
|
* The permissions to be granted are given as bit map in permissionsToBeGrantedBitMap (ex: [.PERMISSIONS_FOR_TAKING_PHOTO]).
|
||||||
|
* <br></br>permissionsToBeGrantedBitMap is passed as the request code in onRequestPermissionsResult().
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* If a permission was already denied by the user, a popup is displayed to
|
||||||
|
* explain why vector needs the corresponding permission.
|
||||||
|
*
|
||||||
|
* @param permissionsToBeGrantedBitMap the permissions bit map to be granted
|
||||||
|
* @param activity the calling Activity that is requesting the permissions (or fragment parent)
|
||||||
|
* @param fragment the calling fragment that is requesting the permissions
|
||||||
|
* @return true if the permissions are granted (synchronous flow), false otherwise (asynchronous flow)
|
||||||
|
*/
|
||||||
|
private fun checkPermissions(permissionsToBeGrantedBitMap: Int,
|
||||||
|
activity: Activity?,
|
||||||
|
fragment: Fragment?,
|
||||||
|
requestCode: Int): Boolean {
|
||||||
|
var isPermissionGranted = false
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
if (null == activity) {
|
||||||
|
Timber.w("## checkPermissions(): invalid input data")
|
||||||
|
isPermissionGranted = false
|
||||||
|
} else if (PERMISSIONS_EMPTY == permissionsToBeGrantedBitMap) {
|
||||||
|
isPermissionGranted = true
|
||||||
|
} else if (PERMISSIONS_FOR_AUDIO_IP_CALL != permissionsToBeGrantedBitMap
|
||||||
|
&& PERMISSIONS_FOR_VIDEO_IP_CALL != permissionsToBeGrantedBitMap
|
||||||
|
&& PERMISSIONS_FOR_TAKING_PHOTO != permissionsToBeGrantedBitMap
|
||||||
|
&& PERMISSIONS_FOR_MEMBERS_SEARCH != permissionsToBeGrantedBitMap
|
||||||
|
&& PERMISSIONS_FOR_MEMBER_DETAILS != permissionsToBeGrantedBitMap
|
||||||
|
&& PERMISSIONS_FOR_ROOM_AVATAR != permissionsToBeGrantedBitMap
|
||||||
|
&& PERMISSIONS_FOR_VIDEO_RECORDING != permissionsToBeGrantedBitMap
|
||||||
|
&& PERMISSIONS_FOR_WRITING_FILES != permissionsToBeGrantedBitMap) {
|
||||||
|
Timber.w("## checkPermissions(): permissions to be granted are not supported")
|
||||||
|
isPermissionGranted = false
|
||||||
|
} else {
|
||||||
|
val permissionListAlreadyDenied = ArrayList<String>()
|
||||||
|
val permissionsListToBeGranted = ArrayList<String>()
|
||||||
|
var isRequestPermissionRequired = false
|
||||||
|
var explanationMessage = ""
|
||||||
|
|
||||||
|
// retrieve the permissions to be granted according to the request code bit map
|
||||||
|
if (PERMISSION_CAMERA == permissionsToBeGrantedBitMap and PERMISSION_CAMERA) {
|
||||||
|
val permissionType = Manifest.permission.CAMERA
|
||||||
|
isRequestPermissionRequired = isRequestPermissionRequired or
|
||||||
|
updatePermissionsToBeGranted(activity, permissionListAlreadyDenied, permissionsListToBeGranted, permissionType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PERMISSION_RECORD_AUDIO == permissionsToBeGrantedBitMap and PERMISSION_RECORD_AUDIO) {
|
||||||
|
val permissionType = Manifest.permission.RECORD_AUDIO
|
||||||
|
isRequestPermissionRequired = isRequestPermissionRequired or
|
||||||
|
updatePermissionsToBeGranted(activity, permissionListAlreadyDenied, permissionsListToBeGranted, permissionType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PERMISSION_WRITE_EXTERNAL_STORAGE == permissionsToBeGrantedBitMap and PERMISSION_WRITE_EXTERNAL_STORAGE) {
|
||||||
|
val permissionType = Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
|
isRequestPermissionRequired = isRequestPermissionRequired or
|
||||||
|
updatePermissionsToBeGranted(activity, permissionListAlreadyDenied, permissionsListToBeGranted, permissionType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// the contact book access is requested for any android platforms
|
||||||
|
// for android M, we use the system preferences
|
||||||
|
// for android < M, we use a dedicated settings
|
||||||
|
if (PERMISSION_READ_CONTACTS == permissionsToBeGrantedBitMap and PERMISSION_READ_CONTACTS) {
|
||||||
|
val permissionType = Manifest.permission.READ_CONTACTS
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
isRequestPermissionRequired = isRequestPermissionRequired or
|
||||||
|
updatePermissionsToBeGranted(activity, permissionListAlreadyDenied, permissionsListToBeGranted, permissionType)
|
||||||
|
} else {
|
||||||
|
// TODO uncomment
|
||||||
|
/*if (!ContactsManager.getInstance().isContactBookAccessRequested) {
|
||||||
|
isRequestPermissionRequired = true
|
||||||
|
permissionsListToBeGranted.add(permissionType)
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if some permissions were already denied: display a dialog to the user before asking again.
|
||||||
|
if (!permissionListAlreadyDenied.isEmpty()) {
|
||||||
|
if (permissionsToBeGrantedBitMap == PERMISSIONS_FOR_VIDEO_IP_CALL || permissionsToBeGrantedBitMap == PERMISSIONS_FOR_AUDIO_IP_CALL) {
|
||||||
|
// Permission request for VOIP call
|
||||||
|
if (permissionListAlreadyDenied.contains(Manifest.permission.CAMERA)
|
||||||
|
&& permissionListAlreadyDenied.contains(Manifest.permission.RECORD_AUDIO)) {
|
||||||
|
// Both missing
|
||||||
|
explanationMessage += activity.getString(R.string.permissions_rationale_msg_camera_and_audio)
|
||||||
|
} else if (permissionListAlreadyDenied.contains(Manifest.permission.RECORD_AUDIO)) {
|
||||||
|
// Audio missing
|
||||||
|
explanationMessage += activity.getString(R.string.permissions_rationale_msg_record_audio)
|
||||||
|
explanationMessage += activity.getString(R.string.permissions_rationale_msg_record_audio_explanation)
|
||||||
|
} else if (permissionListAlreadyDenied.contains(Manifest.permission.CAMERA)) {
|
||||||
|
// Camera missing
|
||||||
|
explanationMessage += activity.getString(R.string.permissions_rationale_msg_camera)
|
||||||
|
explanationMessage += activity.getString(R.string.permissions_rationale_msg_camera_explanation)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
permissionListAlreadyDenied.forEach {
|
||||||
|
when (it) {
|
||||||
|
Manifest.permission.CAMERA -> {
|
||||||
|
if (!TextUtils.isEmpty(explanationMessage)) {
|
||||||
|
explanationMessage += "\n\n"
|
||||||
|
}
|
||||||
|
explanationMessage += activity.getString(R.string.permissions_rationale_msg_camera)
|
||||||
|
}
|
||||||
|
Manifest.permission.RECORD_AUDIO -> {
|
||||||
|
if (!TextUtils.isEmpty(explanationMessage)) {
|
||||||
|
explanationMessage += "\n\n"
|
||||||
|
}
|
||||||
|
explanationMessage += activity.getString(R.string.permissions_rationale_msg_record_audio)
|
||||||
|
}
|
||||||
|
Manifest.permission.WRITE_EXTERNAL_STORAGE -> {
|
||||||
|
if (!TextUtils.isEmpty(explanationMessage)) {
|
||||||
|
explanationMessage += "\n\n"
|
||||||
|
}
|
||||||
|
explanationMessage += activity.getString(R.string.permissions_rationale_msg_storage)
|
||||||
|
}
|
||||||
|
Manifest.permission.READ_CONTACTS -> {
|
||||||
|
if (!TextUtils.isEmpty(explanationMessage)) {
|
||||||
|
explanationMessage += "\n\n"
|
||||||
|
}
|
||||||
|
explanationMessage += activity.getString(R.string.permissions_rationale_msg_contacts)
|
||||||
|
}
|
||||||
|
else -> Timber.d("## checkPermissions(): already denied permission not supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// display the dialog with the info text
|
||||||
|
AlertDialog.Builder(activity)
|
||||||
|
.setTitle(R.string.permissions_rationale_popup_title)
|
||||||
|
.setMessage(explanationMessage)
|
||||||
|
.setOnCancelListener { Toast.makeText(activity, R.string.missing_permissions_warning, Toast.LENGTH_SHORT).show() }
|
||||||
|
.setPositiveButton(R.string.ok) { _, _ ->
|
||||||
|
if (!permissionsListToBeGranted.isEmpty()) {
|
||||||
|
fragment?.requestPermissions(permissionsListToBeGranted.toTypedArray(), requestCode)
|
||||||
|
?: run {
|
||||||
|
ActivityCompat.requestPermissions(activity, permissionsListToBeGranted.toTypedArray(), requestCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
} else {
|
||||||
|
// some permissions are not granted, ask permissions
|
||||||
|
if (isRequestPermissionRequired) {
|
||||||
|
val permissionsArrayToBeGranted = permissionsListToBeGranted.toTypedArray()
|
||||||
|
|
||||||
|
// for android < M, we use a custom dialog to request the contacts book access.
|
||||||
|
/*
|
||||||
|
if (permissionsListToBeGranted.contains(Manifest.permission.READ_CONTACTS)
|
||||||
|
&& Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||||
|
AlertDialog.Builder(activity)
|
||||||
|
.setIcon(android.R.drawable.ic_dialog_info)
|
||||||
|
.setTitle(R.string.permissions_rationale_popup_title)
|
||||||
|
.setMessage(R.string.permissions_msg_contacts_warning_other_androids)
|
||||||
|
// gives the contacts book access
|
||||||
|
.setPositiveButton(R.string.yes) { _, _ ->
|
||||||
|
ContactsManager.getInstance().setIsContactBookAccessAllowed(true)
|
||||||
|
fragment?.requestPermissions(permissionsArrayToBeGranted, requestCode)
|
||||||
|
?: run {
|
||||||
|
ActivityCompat.requestPermissions(activity, permissionsArrayToBeGranted, requestCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// or reject it
|
||||||
|
.setNegativeButton(R.string.no) { _, _ ->
|
||||||
|
ContactsManager.getInstance().setIsContactBookAccessAllowed(false)
|
||||||
|
fragment?.requestPermissions(permissionsArrayToBeGranted, requestCode)
|
||||||
|
?: run {
|
||||||
|
ActivityCompat.requestPermissions(activity, permissionsArrayToBeGranted, requestCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
} else {
|
||||||
|
fragment?.requestPermissions(permissionsArrayToBeGranted, requestCode)
|
||||||
|
?: run {
|
||||||
|
ActivityCompat.requestPermissions(activity, permissionsArrayToBeGranted, requestCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
} else {
|
||||||
|
// permissions were granted, start now.
|
||||||
|
isPermissionGranted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isPermissionGranted
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method used in [.checkPermissions] to populate the list of the
|
||||||
|
* permissions to be granted (permissionsListToBeGranted_out) and the list of the permissions already denied (permissionAlreadyDeniedList_out).
|
||||||
|
*
|
||||||
|
* @param activity calling activity
|
||||||
|
* @param permissionAlreadyDeniedList_out list to be updated with the permissions already denied by the user
|
||||||
|
* @param permissionsListToBeGranted_out list to be updated with the permissions to be granted
|
||||||
|
* @param permissionType the permission to be checked
|
||||||
|
* @return true if the permission requires to be granted, false otherwise
|
||||||
|
*/
|
||||||
|
private fun updatePermissionsToBeGranted(activity: Activity,
|
||||||
|
permissionAlreadyDeniedList_out: MutableList<String>,
|
||||||
|
permissionsListToBeGranted_out: MutableList<String>,
|
||||||
|
permissionType: String): Boolean {
|
||||||
|
var isRequestPermissionRequested = false
|
||||||
|
|
||||||
|
// add permission to be granted
|
||||||
|
permissionsListToBeGranted_out.add(permissionType)
|
||||||
|
|
||||||
|
if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(activity.applicationContext, permissionType)) {
|
||||||
|
isRequestPermissionRequested = true
|
||||||
|
|
||||||
|
// add permission to the ones that were already asked to the user
|
||||||
|
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permissionType)) {
|
||||||
|
permissionAlreadyDeniedList_out.add(permissionType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isRequestPermissionRequested
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to process [.PERMISSIONS_FOR_AUDIO_IP_CALL]
|
||||||
|
* on onRequestPermissionsResult() methods.
|
||||||
|
*
|
||||||
|
* @param context App context
|
||||||
|
* @param grantResults permissions granted results
|
||||||
|
* @return true if audio IP call is permitted, false otherwise
|
||||||
|
*/
|
||||||
|
fun onPermissionResultAudioIpCall(context: Context, grantResults: IntArray): Boolean {
|
||||||
|
val arePermissionsGranted = allGranted(grantResults)
|
||||||
|
|
||||||
|
if (!arePermissionsGranted) {
|
||||||
|
Toast.makeText(context, R.string.permissions_action_not_performed_missing_permissions, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
return arePermissionsGranted
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to process [.PERMISSIONS_FOR_VIDEO_IP_CALL]
|
||||||
|
* on onRequestPermissionsResult() methods.
|
||||||
|
* For video IP calls, record audio and camera permissions are both mandatory.
|
||||||
|
*
|
||||||
|
* @param context App context
|
||||||
|
* @param grantResults permissions granted results
|
||||||
|
* @return true if video IP call is permitted, false otherwise
|
||||||
|
*/
|
||||||
|
fun onPermissionResultVideoIpCall(context: Context, grantResults: IntArray): Boolean {
|
||||||
|
val arePermissionsGranted = allGranted(grantResults)
|
||||||
|
|
||||||
|
if (!arePermissionsGranted) {
|
||||||
|
Toast.makeText(context, R.string.permissions_action_not_performed_missing_permissions, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
return arePermissionsGranted
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if all permissions are granted, false if not or if permission request has been cancelled
|
||||||
|
*/
|
||||||
|
fun allGranted(grantResults: IntArray): Boolean {
|
||||||
|
if (grantResults.isEmpty()) {
|
||||||
|
// A cancellation occurred
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var granted = true
|
||||||
|
|
||||||
|
grantResults.forEach {
|
||||||
|
granted = granted && PackageManager.PERMISSION_GRANTED == it
|
||||||
|
}
|
||||||
|
|
||||||
|
return granted
|
||||||
|
}
|
@ -21,15 +21,23 @@ import android.os.Parcelable
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.airbnb.epoxy.EpoxyVisibilityTracker
|
import com.airbnb.epoxy.EpoxyVisibilityTracker
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.dialogs.DialogListItem
|
||||||
|
import im.vector.riotredesign.core.dialogs.DialogSendItemAdapter
|
||||||
import im.vector.riotredesign.core.epoxy.LayoutManagerStateRestorer
|
import im.vector.riotredesign.core.epoxy.LayoutManagerStateRestorer
|
||||||
import im.vector.riotredesign.core.platform.RiotFragment
|
import im.vector.riotredesign.core.platform.RiotFragment
|
||||||
import im.vector.riotredesign.core.platform.ToolbarConfigurable
|
import im.vector.riotredesign.core.platform.ToolbarConfigurable
|
||||||
|
import im.vector.riotredesign.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
|
||||||
|
import im.vector.riotredesign.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA
|
||||||
|
import im.vector.riotredesign.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_CAMERA
|
||||||
|
import im.vector.riotredesign.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_VIDEO_CAMERA
|
||||||
|
import im.vector.riotredesign.core.utils.checkPermissions
|
||||||
import im.vector.riotredesign.features.home.AvatarRenderer
|
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||||
import im.vector.riotredesign.features.home.HomeModule
|
import im.vector.riotredesign.features.home.HomeModule
|
||||||
import im.vector.riotredesign.features.home.HomePermalinkHandler
|
import im.vector.riotredesign.features.home.HomePermalinkHandler
|
||||||
@ -79,6 +87,7 @@ class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback {
|
|||||||
setupRecyclerView()
|
setupRecyclerView()
|
||||||
setupToolbar()
|
setupToolbar()
|
||||||
setupSendButton()
|
setupSendButton()
|
||||||
|
setupAttachmentButton()
|
||||||
roomDetailViewModel.subscribe { renderState(it) }
|
roomDetailViewModel.subscribe { renderState(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,6 +137,61 @@ class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setupAttachmentButton() {
|
||||||
|
attachmentButton.setOnClickListener {
|
||||||
|
val items = ArrayList<DialogListItem>()
|
||||||
|
// Send file
|
||||||
|
items.add(DialogListItem.SendFile)
|
||||||
|
// Send voice
|
||||||
|
/*
|
||||||
|
if (PreferencesManager.isSendVoiceFeatureEnabled(this)) {
|
||||||
|
items.add(DialogListItem.SendVoice.INSTANCE)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Send sticker
|
||||||
|
items.add(DialogListItem.SendSticker)
|
||||||
|
// Camera
|
||||||
|
|
||||||
|
//if (PreferencesManager.useNativeCamera(this)) {
|
||||||
|
items.add(DialogListItem.TakePhoto)
|
||||||
|
items.add(DialogListItem.TakeVideo)
|
||||||
|
//} else {
|
||||||
|
// items.add(DialogListItem.TakePhotoVideo.INSTANCE)
|
||||||
|
// }
|
||||||
|
val adapter = DialogSendItemAdapter(requireContext(), items)
|
||||||
|
AlertDialog.Builder(requireContext())
|
||||||
|
.setAdapter(adapter, { dialog, which ->
|
||||||
|
onSendChoiceClicked(items[which])
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onSendChoiceClicked(dialogListItem: DialogListItem) {
|
||||||
|
when (dialogListItem) {
|
||||||
|
is DialogListItem.SendFile -> {
|
||||||
|
//launchFileSelectionIntent()
|
||||||
|
}
|
||||||
|
is DialogListItem.SendVoice -> {
|
||||||
|
//launchAudioRecorderIntent()
|
||||||
|
}
|
||||||
|
is DialogListItem.SendSticker -> {
|
||||||
|
//startStickerPickerActivity()
|
||||||
|
}
|
||||||
|
is DialogListItem.TakePhotoVideo -> if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) {
|
||||||
|
// launchCamera()
|
||||||
|
}
|
||||||
|
is DialogListItem.TakePhoto -> if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_CAMERA)) {
|
||||||
|
// launchNativeCamera()
|
||||||
|
}
|
||||||
|
is DialogListItem.TakeVideo -> if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_VIDEO_CAMERA)) {
|
||||||
|
// launchNativeVideoRecorder()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun renderState(state: RoomDetailViewState) {
|
private fun renderState(state: RoomDetailViewState) {
|
||||||
renderRoomSummary(state)
|
renderRoomSummary(state)
|
||||||
timelineEventController.setTimeline(state.timeline)
|
timelineEventController.setTimeline(state.timeline)
|
||||||
|
BIN
vector/src/main/res/drawable-hdpi/ic_attach_file_white.png
Normal file
After Width: | Height: | Size: 394 B |
BIN
vector/src/main/res/drawable-mdpi/ic_attach_file_white.png
Normal file
After Width: | Height: | Size: 285 B |
BIN
vector/src/main/res/drawable-mdpi/ic_material_camera.png
Executable file
After Width: | Height: | Size: 539 B |
BIN
vector/src/main/res/drawable-mdpi/ic_material_file.png
Executable file
After Width: | Height: | Size: 545 B |
BIN
vector/src/main/res/drawable-mdpi/ic_material_videocam.png
Executable file
After Width: | Height: | Size: 3.0 KiB |
BIN
vector/src/main/res/drawable-mdpi/ic_send_sticker.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
BIN
vector/src/main/res/drawable-mdpi/vector_micro_green.png
Executable file
After Width: | Height: | Size: 399 B |
BIN
vector/src/main/res/drawable-mdpi/video_call_black.png
Executable file
After Width: | Height: | Size: 224 B |
BIN
vector/src/main/res/drawable-mdpi/video_call_green.png
Executable file
After Width: | Height: | Size: 298 B |
BIN
vector/src/main/res/drawable-mdpi/voice_call_black.png
Executable file
After Width: | Height: | Size: 574 B |
BIN
vector/src/main/res/drawable-mdpi/voice_call_end_fushia.png
Executable file
After Width: | Height: | Size: 331 B |
BIN
vector/src/main/res/drawable-mdpi/voice_call_green.png
Executable file
After Width: | Height: | Size: 432 B |
BIN
vector/src/main/res/drawable-mdpi/voice_call_start_green.png
Executable file
After Width: | Height: | Size: 684 B |
BIN
vector/src/main/res/drawable-xhdpi/ic_attach_file_white.png
Normal file
After Width: | Height: | Size: 507 B |
BIN
vector/src/main/res/drawable-xxhdpi/ic_attach_file_white.png
Normal file
After Width: | Height: | Size: 809 B |
BIN
vector/src/main/res/drawable-xxxhdpi/ic_attach_file_white.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
62
vector/src/main/res/layout/dialog_export_e2e_keys.xml
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/layout_root"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingStart="?dialogPreferredPadding"
|
||||||
|
android:paddingLeft="?dialogPreferredPadding"
|
||||||
|
android:paddingTop="12dp"
|
||||||
|
android:paddingEnd="?dialogPreferredPadding"
|
||||||
|
android:paddingRight="?dialogPreferredPadding"
|
||||||
|
android:paddingBottom="12dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/encryption_export_notice"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:textColorHint="?attr/vctr_default_text_hint_color">
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputEditText
|
||||||
|
android:id="@+id/dialog_e2e_keys_passphrase_edit_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/passphrase_create_passphrase"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:textColor="?android:textColorPrimary" />
|
||||||
|
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:id="@+id/dialog_e2e_keys_confirm_passphrase_til"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:textColorHint="?attr/vctr_default_text_hint_color"
|
||||||
|
app:errorEnabled="true">
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputEditText
|
||||||
|
android:id="@+id/dialog_e2e_keys_confirm_passphrase_edit_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/passphrase_confirm_passphrase"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:textColor="?android:textColorPrimary" />
|
||||||
|
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/dialog_e2e_keys_export_button"
|
||||||
|
style="@style/VectorButtonStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:enabled="false"
|
||||||
|
android:text="@string/encryption_export_export" />
|
||||||
|
</LinearLayout>
|
@ -93,6 +93,17 @@
|
|||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent">
|
app:layout_constraintStart_toStartOf="parent">
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/attachmentButton"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_toStartOf="@id/sendButton"
|
||||||
|
android:layout_toLeftOf="@id/sendButton"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:src="@drawable/ic_attach_file_white"
|
||||||
|
android:tint="?attr/colorAccent" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/sendButton"
|
android:id="@+id/sendButton"
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
@ -111,8 +122,8 @@
|
|||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:layout_toStartOf="@id/sendButton"
|
android:layout_toStartOf="@id/attachmentButton"
|
||||||
android:layout_toLeftOf="@id/sendButton"
|
android:layout_toLeftOf="@id/attachmentButton"
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:hint="@string/room_message_placeholder_not_encrypted"
|
android:hint="@string/room_message_placeholder_not_encrypted"
|
||||||
|
53
vector/src/main/res/layout/item_dialog.xml
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
~ Copyright 2019 New Vector Ltd
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="48dp"
|
||||||
|
android:paddingLeft="8dp"
|
||||||
|
android:paddingRight="8dp">
|
||||||
|
|
||||||
|
<!-- Do not use drawableStart for icon size and for RTL -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/adapter_item_dialog_icon"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:rotationY="@integer/rtl_mirror_flip"
|
||||||
|
android:tint="?attr/colorAccent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:src="@drawable/video_call_green" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/adapter_item_dialog_text"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:textSize="20sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/adapter_item_dialog_icon"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="@string/action_video_call" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|