From 907a1d1a4bb04c31ad70ec0c585f57318fd415da Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Jun 2019 12:27:29 +0200 Subject: [PATCH] Import keys: WIP --- .../core/intent/ExternalIntentAnalyser.kt | 152 ++++++++++++++ .../riotredesign/core/intent/Filename.kt | 44 ++++ .../core/intent/VectorMimeType.kt | 54 +++++ .../VectorSettingsPreferencesFragment.kt | 197 ++++++++++-------- .../res/layout/dialog_import_e2e_keys.xml | 13 ++ vector/src/main/res/values/strings_riotX.xml | 2 + 6 files changed, 374 insertions(+), 88 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotredesign/core/intent/ExternalIntentAnalyser.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/core/intent/Filename.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/core/intent/VectorMimeType.kt diff --git a/vector/src/main/java/im/vector/riotredesign/core/intent/ExternalIntentAnalyser.kt b/vector/src/main/java/im/vector/riotredesign/core/intent/ExternalIntentAnalyser.kt new file mode 100644 index 00000000..916662e3 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/core/intent/ExternalIntentAnalyser.kt @@ -0,0 +1,152 @@ +/* + * 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.intent + +import android.content.ClipData +import android.content.ClipDescription +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.text.TextUtils +import androidx.core.util.PatternsCompat.WEB_URL +import java.util.* + +/** + * Inspired from Riot code: RoomMediaMessage.java + */ +sealed class ExternalIntentData { + /** + * Constructor for a text message. + * + * @param text the text + * @param htmlText the HTML text + * @param format the formatted text format + */ + data class IntentDataText( + val text: CharSequence? = null, + val htmlText: String? = null, + val format: String? = null, + val clipDataItem: ClipData.Item = ClipData.Item(text, htmlText), + val mimeType: String? = if (null == htmlText) ClipDescription.MIMETYPE_TEXT_PLAIN else format + ) : ExternalIntentData() + + /** + * Clip data + */ + data class IntentDataClipData( + val clipDataItem: ClipData.Item, + val mimeType: String? + ) : ExternalIntentData() + + /** + * Constructor from a media Uri/ + * + * @param uri the media uri + * @param filename the media file name + */ + data class IntentDataUri( + val uri: Uri, + val filename: String? = null + ) : ExternalIntentData() +} + + +fun analyseIntent(intent: Intent): List { + val externalIntentDataList = ArrayList() + + + // chrome adds many items when sharing an web page link + // so, test first the type + if (TextUtils.equals(intent.type, ClipDescription.MIMETYPE_TEXT_PLAIN)) { + var message: String? = intent.getStringExtra(Intent.EXTRA_TEXT) + + if (null == message) { + val sequence = intent.getCharSequenceExtra(Intent.EXTRA_TEXT) + if (null != sequence) { + message = sequence.toString() + } + } + + val subject = intent.getStringExtra(Intent.EXTRA_SUBJECT) + + if (!TextUtils.isEmpty(subject)) { + if (TextUtils.isEmpty(message)) { + message = subject + } else if (WEB_URL.matcher(message!!).matches()) { + message = subject + "\n" + message + } + } + + if (!TextUtils.isEmpty(message)) { + externalIntentDataList.add(ExternalIntentData.IntentDataText(message!!, null, intent.type)) + return externalIntentDataList + } + } + + var clipData: ClipData? = null + var mimetypes: MutableList? = null + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + clipData = intent.clipData + } + + // multiple data + if (null != clipData) { + if (null != clipData.description) { + if (0 != clipData.description.mimeTypeCount) { + mimetypes = ArrayList() + + for (i in 0 until clipData.description.mimeTypeCount) { + mimetypes.add(clipData.description.getMimeType(i)) + } + + // if the filter is "accept anything" the mimetype does not make sense + if (1 == mimetypes.size) { + if (mimetypes[0].endsWith("/*")) { + mimetypes = null + } + } + } + } + + val count = clipData.itemCount + + for (i in 0 until count) { + val item = clipData.getItemAt(i) + var mimetype: String? = null + + if (null != mimetypes) { + if (i < mimetypes.size) { + mimetype = mimetypes[i] + } else { + mimetype = mimetypes[0] + } + + // uris list is not a valid mimetype + if (TextUtils.equals(mimetype, ClipDescription.MIMETYPE_TEXT_URILIST)) { + mimetype = null + } + } + + externalIntentDataList.add(ExternalIntentData.IntentDataClipData(item, mimetype)) + } + } else if (null != intent.data) { + externalIntentDataList.add(ExternalIntentData.IntentDataUri(intent.data!!)) + } + + return externalIntentDataList +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/core/intent/Filename.kt b/vector/src/main/java/im/vector/riotredesign/core/intent/Filename.kt new file mode 100644 index 00000000..eba9d01b --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/core/intent/Filename.kt @@ -0,0 +1,44 @@ +/* + * 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.intent + +import android.content.Context +import android.database.Cursor +import android.net.Uri +import android.provider.OpenableColumns + +fun getFilenameFromUri(context: Context, uri: Uri): String? { + var result: String? = null + if (uri.scheme == "content") { + val cursor: Cursor? = context.contentResolver.query(uri, null, null, null, null) + try { + if (cursor != null && cursor.moveToFirst()) { + result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)) + } + } finally { + cursor?.close() + } + } + if (result == null) { + result = uri.path + val cut = result.lastIndexOf('/') + if (cut != -1) { + result = result.substring(cut + 1) + } + } + return result +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/core/intent/VectorMimeType.kt b/vector/src/main/java/im/vector/riotredesign/core/intent/VectorMimeType.kt new file mode 100644 index 00000000..b801d2c2 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/core/intent/VectorMimeType.kt @@ -0,0 +1,54 @@ +/* + * 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.intent + +import android.content.Context +import android.net.Uri +import android.webkit.MimeTypeMap +import im.vector.riotredesign.core.utils.getFileExtension +import timber.log.Timber + +/** + * Returns the mimetype from a uri. + * + * @param context the context + * @return the mimetype + */ +fun getMimeTypeFromUri(context: Context, uri: Uri): String? { + var mimeType: String? = null + + try { + mimeType = context.contentResolver.getType(uri) + + // try to find the mimetype from the filename + if (null == mimeType) { + val extension = getFileExtension(uri.toString()) + if (extension != null) { + mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) + } + } + + if (null != mimeType) { + // the mimetype is sometimes in uppercase. + mimeType = mimeType.toLowerCase() + } + } catch (e: Exception) { + Timber.e(e, "Failed to open resource input stream") + } + + return mimeType +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsPreferencesFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsPreferencesFragment.kt index 3caad728..23a63869 100755 --- a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsPreferencesFragment.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsPreferencesFragment.kt @@ -49,18 +49,24 @@ import im.vector.matrix.android.api.extensions.sortByLastSeen import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.internal.auth.data.LoginFlowTypes +import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse import im.vector.riotredesign.R import im.vector.riotredesign.core.dialogs.ExportKeysDialog import im.vector.riotredesign.core.extensions.showPassword import im.vector.riotredesign.core.extensions.withArgs +import im.vector.riotredesign.core.intent.ExternalIntentData +import im.vector.riotredesign.core.intent.analyseIntent +import im.vector.riotredesign.core.intent.getFilenameFromUri +import im.vector.riotredesign.core.intent.getMimeTypeFromUri import im.vector.riotredesign.core.platform.SimpleTextWatcher import im.vector.riotredesign.core.platform.VectorPreferenceFragment import im.vector.riotredesign.core.preference.BingRule import im.vector.riotredesign.core.preference.ProgressBarPreference import im.vector.riotredesign.core.preference.UserAvatarPreference import im.vector.riotredesign.core.preference.VectorPreference +import im.vector.riotredesign.core.resources.openResource import im.vector.riotredesign.core.utils.* import im.vector.riotredesign.features.MainActivity import im.vector.riotredesign.features.configuration.VectorConfiguration @@ -2643,100 +2649,115 @@ class VectorSettingsPreferencesFragment : VectorPreferenceFragment(), SharedPref return } - notImplemented() + val sharedDataItems = analyseIntent(intent) + val thisActivity = activity - /* -val sharedDataItems = ArrayList(RoomMediaMessage.listRoomMediaMessages(intent)) -val thisActivity = activity + if (sharedDataItems.isNotEmpty() && thisActivity != null) { + val sharedDataItem = sharedDataItems[0] -if (sharedDataItems.isNotEmpty() && thisActivity != null) { - val sharedDataItem = sharedDataItems[0] - val dialogLayout = thisActivity.layoutInflater.inflate(R.layout.dialog_import_e2e_keys, null) - val builder = AlertDialog.Builder(thisActivity) - .setTitle(R.string.encryption_import_room_keys) - .setView(dialogLayout) - - val passPhraseEditText = dialogLayout.findViewById(R.id.dialog_e2e_keys_passphrase_edit_text) - val importButton = dialogLayout.findViewById