forked from upstream-mirrors/quik
Merge pull request #254 from gavine99/mms-attachment-share-242
fixes for mms attachment view/share is suboptimal (#242) and other small fixups
This commit is contained in:
@@ -21,14 +21,14 @@ package dev.octoshrimpy.quik.extensions
|
||||
import com.google.android.mms.ContentType
|
||||
import dev.octoshrimpy.quik.model.MmsPart
|
||||
|
||||
fun MmsPart.isSmil() = ContentType.APP_SMIL == type
|
||||
fun MmsPart.isSmil() = ContentType.APP_SMIL.lowercase() == type.lowercase()
|
||||
|
||||
fun MmsPart.isImage() = ContentType.isImageType(type)
|
||||
fun MmsPart.isImage() = ContentType.isImageType(type.lowercase())
|
||||
|
||||
fun MmsPart.isVideo() = ContentType.isVideoType(type)
|
||||
fun MmsPart.isVideo() = ContentType.isVideoType(type.lowercase())
|
||||
|
||||
fun MmsPart.isAudio() = ContentType.isAudioType(type)
|
||||
fun MmsPart.isAudio() = ContentType.isAudioType(type.lowercase())
|
||||
|
||||
fun MmsPart.isText() = ContentType.TEXT_PLAIN == type
|
||||
fun MmsPart.isText() = ContentType.TEXT_PLAIN.lowercase() == type.lowercase()
|
||||
|
||||
fun MmsPart.isVCard() = ContentType.TEXT_VCARD == type
|
||||
fun MmsPart.isVCard() = ContentType.TEXT_VCARD.lowercase() == type.lowercase()
|
||||
|
||||
@@ -443,8 +443,8 @@ class MessageRepositoryImpl @Inject constructor(
|
||||
|
||||
imageBytesByAttachment.forEach { (attachment, bytes) ->
|
||||
parts += when (attachment.getType(context) == "image/gif") {
|
||||
true -> MMSPart("image", ContentType.IMAGE_GIF, bytes)
|
||||
false -> MMSPart("image", ContentType.IMAGE_JPEG, bytes)
|
||||
true -> MMSPart(attachment.getName(context), ContentType.IMAGE_GIF, bytes)
|
||||
false -> MMSPart(attachment.getName(context), ContentType.IMAGE_JPEG, bytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,4 +16,14 @@
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<manifest />
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application>
|
||||
<provider
|
||||
android:name="com.moez.QKSMS.contentproviders.MmsPartProvider"
|
||||
android:authorities="dev.octoshrimpy.quik.mmspart"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
</provider>
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
*
|
||||
* This file is part of QKSMS.
|
||||
*
|
||||
* QKSMS 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.
|
||||
*
|
||||
* QKSMS 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 QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.moez.QKSMS.contentproviders
|
||||
|
||||
import android.content.ContentProvider
|
||||
import android.content.ContentResolver
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.ParcelFileDescriptor
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
|
||||
// the primary use case for this provider is to re-provide mms attachments but with a file name
|
||||
// as the last part of the uri as opposed to the system mms provider uri which has a part id as
|
||||
// it's 'filename' component. with this provider external applications can save/display using the
|
||||
// original file name, if available, of the attachment when it is shared and/or saved
|
||||
// it seems a needlessly complicated way to achieve the requirement, but it has to be done
|
||||
class MmsPartProvider : ContentProvider() {
|
||||
companion object {
|
||||
private fun getMmsPartIdFromUri(uri: Uri): String? {
|
||||
// ensure at least 2 path segments exist
|
||||
if (uri.pathSegments.size < 2)
|
||||
return null
|
||||
|
||||
return try {
|
||||
return uri.pathSegments[uri.pathSegments.size - 2]
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getUnderlyingUriFromUri(uri: Uri): Uri {
|
||||
val partId = getMmsPartIdFromUri(uri) ?: return Uri.EMPTY
|
||||
return Uri
|
||||
.Builder()
|
||||
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority("mms")
|
||||
.encodedPath("part/$partId")
|
||||
.build()
|
||||
}
|
||||
|
||||
fun getUriForMmsPartId(partId: Long, partName: String): Uri {
|
||||
return Uri
|
||||
.Builder()
|
||||
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority("dev.octoshrimpy.quik.mmspart")
|
||||
.encodedPath("part/$partId/$partName")
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun query(
|
||||
uri: Uri,
|
||||
projection: Array<String>?,
|
||||
selection: String?,
|
||||
selectionArgs: Array<String>?,
|
||||
sortOrder: String?
|
||||
): Cursor? {
|
||||
return context?.contentResolver?.query(
|
||||
getUnderlyingUriFromUri(uri),
|
||||
projection,
|
||||
selection,
|
||||
selectionArgs,
|
||||
sortOrder
|
||||
)
|
||||
}
|
||||
|
||||
override fun getType(uri: Uri): String? {
|
||||
return context?.contentResolver?.getType(getUnderlyingUriFromUri(uri))
|
||||
}
|
||||
|
||||
override fun insert(uri: Uri, values: ContentValues?): Uri? {
|
||||
return context?.contentResolver?.insert(getUnderlyingUriFromUri(uri), values)
|
||||
}
|
||||
|
||||
override fun update(
|
||||
uri: Uri,
|
||||
values: ContentValues?,
|
||||
selection: String?,
|
||||
selectionArgs: Array<String>?
|
||||
): Int {
|
||||
return context?.contentResolver?.update(
|
||||
getUnderlyingUriFromUri(uri),
|
||||
values,
|
||||
selection,
|
||||
selectionArgs
|
||||
) ?: 0
|
||||
}
|
||||
|
||||
override fun delete(
|
||||
uri: Uri,
|
||||
selection: String?,
|
||||
selectionArgs: Array<String>?
|
||||
): Int {
|
||||
return context?.contentResolver?.delete(
|
||||
getUnderlyingUriFromUri(uri),
|
||||
selection,
|
||||
selectionArgs
|
||||
) ?: 0
|
||||
}
|
||||
|
||||
@Throws(FileNotFoundException::class)
|
||||
override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? {
|
||||
return context?.contentResolver?.openFileDescriptor(
|
||||
getUnderlyingUriFromUri(uri),
|
||||
mode)
|
||||
}
|
||||
}
|
||||
@@ -18,12 +18,17 @@
|
||||
*/
|
||||
package dev.octoshrimpy.quik.model
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.content.ContentResolver.MimeTypeInfo
|
||||
import android.net.Uri
|
||||
import android.webkit.MimeTypeMap
|
||||
import androidx.core.net.toUri
|
||||
import io.realm.RealmObject
|
||||
import io.realm.RealmResults
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.LinkingObjects
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import java.io.File
|
||||
|
||||
open class MmsPart : RealmObject() {
|
||||
|
||||
@@ -37,13 +42,23 @@ open class MmsPart : RealmObject() {
|
||||
@LinkingObjects("parts")
|
||||
val messages: RealmResults<Message>? = null
|
||||
|
||||
fun getUri() = "content://mms/part/$id".toUri()
|
||||
fun getUri(): Uri = Uri
|
||||
.Builder()
|
||||
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority("mms")
|
||||
.encodedPath("part/$id")
|
||||
.build()
|
||||
|
||||
fun getBestFilename(): String =
|
||||
if (File(name).extension.isNotEmpty()) name ?: "unknown"
|
||||
else "$name." + (MimeTypeMap.getSingleton().getExtensionFromMimeType(type)
|
||||
?: type.substringAfter("/"))
|
||||
|
||||
fun getSummary(): String? = when {
|
||||
type == "application/smil" -> null
|
||||
type == "text/plain" -> text
|
||||
type == "text/x-vcard" -> "Contact card"
|
||||
type.startsWith("image") -> "Photo"
|
||||
type.startsWith("image") -> "Picture"
|
||||
type.startsWith("video") -> "Video"
|
||||
type.startsWith("audio") -> "Audio"
|
||||
else -> type.substring(type.indexOf('/') + 1)
|
||||
|
||||
@@ -252,17 +252,6 @@
|
||||
<meta-data
|
||||
android:name="preloaded_fonts"
|
||||
android:resource="@array/preloaded_fonts" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/provider_paths" />
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
|
||||
<queries>
|
||||
|
||||
@@ -90,6 +90,8 @@ class AttachmentAdapter @Inject constructor(
|
||||
return
|
||||
}
|
||||
|
||||
holder.fileName.visibility = View.GONE
|
||||
|
||||
// if attachment uri is missing
|
||||
if (!attachment.getUri().resourceExists(context)) {
|
||||
holder.thumbnail.setImageResource(android.R.drawable.ic_delete)
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.os.Vibrator
|
||||
import android.provider.ContactsContract
|
||||
import android.telephony.SmsMessage
|
||||
import androidx.core.content.getSystemService
|
||||
import com.moez.QKSMS.contentproviders.MmsPartProvider
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.Navigator
|
||||
import dev.octoshrimpy.quik.common.base.QkViewModel
|
||||
@@ -470,12 +471,11 @@ class ComposeViewModel @Inject constructor(
|
||||
.mapNotNull(messageRepo::getPart)
|
||||
.filter { part -> !part.isImage() && !part.isVideo() }
|
||||
.autoDisposable(view.scope())
|
||||
.subscribe { part ->
|
||||
if (permissionManager.hasStorage()) {
|
||||
messageRepo.savePart(part.id)?.let { navigator.viewFile(it, part.type) }
|
||||
} else {
|
||||
view.requestStoragePermission()
|
||||
}
|
||||
.subscribe {
|
||||
navigator.viewFile(
|
||||
MmsPartProvider.getUriForMmsPartId(it.id, it.getBestFilename()),
|
||||
it.type
|
||||
)
|
||||
}
|
||||
|
||||
// Update the State when the message selected count changes
|
||||
|
||||
@@ -25,7 +25,9 @@ import android.media.MediaMetadataRetriever
|
||||
import android.view.View
|
||||
import android.widget.SeekBar
|
||||
import com.moez.QKSMS.common.QkMediaPlayer
|
||||
import com.moez.QKSMS.contentproviders.MmsPartProvider
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.Navigator
|
||||
import dev.octoshrimpy.quik.common.base.QkViewHolder
|
||||
import dev.octoshrimpy.quik.common.util.Colors
|
||||
import dev.octoshrimpy.quik.common.util.extensions.resolveThemeColor
|
||||
@@ -49,6 +51,12 @@ import javax.inject.Inject
|
||||
class AudioBinder @Inject constructor(colors: Colors, private val context: Context) :
|
||||
PartBinder() {
|
||||
|
||||
companion object {
|
||||
const val DEFAULT_SHARE_FILENAME = "quik-audio-attachment.mp3"
|
||||
}
|
||||
|
||||
@Inject lateinit var navigator: Navigator
|
||||
|
||||
override val partLayout = R.layout.mms_audio_preview_list_item
|
||||
override var theme = colors.theme()
|
||||
|
||||
@@ -169,7 +177,13 @@ class AudioBinder @Inject constructor(colors: Colors, private val context: Conte
|
||||
}
|
||||
}
|
||||
|
||||
holder.containerView.setOnClickListener { clicks.onNext(part.id) }
|
||||
// share button click handling
|
||||
holder.share.setOnClickListener {
|
||||
navigator.shareFile(
|
||||
MmsPartProvider.getUriForMmsPartId(part.id, part.getBestFilename()),
|
||||
part.type
|
||||
)
|
||||
}
|
||||
|
||||
// if this item is the active active audio item update the active view holder
|
||||
if (audioState.partId == part.id)
|
||||
@@ -224,6 +238,10 @@ class AudioBinder @Inject constructor(colors: Colors, private val context: Conte
|
||||
setBackgroundTint(primaryColor)
|
||||
}
|
||||
|
||||
// share button
|
||||
holder.share.setTint(secondaryColor)
|
||||
holder.share.setBackgroundTint(primaryColor)
|
||||
|
||||
MediaMetadataRetriever().use {
|
||||
it.setDataSource(context, part.getUri())
|
||||
|
||||
|
||||
@@ -22,7 +22,9 @@ import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.view.Gravity
|
||||
import android.widget.FrameLayout
|
||||
import com.moez.QKSMS.contentproviders.MmsPartProvider
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.Navigator
|
||||
import dev.octoshrimpy.quik.common.base.QkViewHolder
|
||||
import dev.octoshrimpy.quik.common.util.Colors
|
||||
import dev.octoshrimpy.quik.common.util.extensions.resolveThemeColor
|
||||
@@ -34,11 +36,14 @@ import dev.octoshrimpy.quik.model.MmsPart
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.android.synthetic.main.mms_audio_preview_list_item.share
|
||||
import kotlinx.android.synthetic.main.mms_file_list_item.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class FileBinder @Inject constructor(colors: Colors, private val context: Context) : PartBinder() {
|
||||
|
||||
@Inject lateinit var navigator: Navigator
|
||||
|
||||
override val partLayout = R.layout.mms_file_list_item
|
||||
override var theme = colors.theme()
|
||||
|
||||
@@ -56,8 +61,6 @@ class FileBinder @Inject constructor(colors: Colors, private val context: Contex
|
||||
BubbleUtils.getBubble(false, canGroupWithPrevious, canGroupWithNext, message.isMe())
|
||||
.let(holder.fileBackground::setBackgroundResource)
|
||||
|
||||
holder.containerView.setOnClickListener { clicks.onNext(part.id) }
|
||||
|
||||
Observable.just(part.getUri())
|
||||
.map(context.contentResolver::openInputStream)
|
||||
.map { inputStream -> inputStream.use { it.available() } }
|
||||
@@ -73,6 +76,14 @@ class FileBinder @Inject constructor(colors: Colors, private val context: Contex
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { size -> holder.size.text = size }
|
||||
|
||||
// share button click handling
|
||||
holder.share.setOnClickListener {
|
||||
navigator.shareFile(
|
||||
MmsPartProvider.getUriForMmsPartId(part.id, part.getBestFilename()),
|
||||
part.type
|
||||
)
|
||||
}
|
||||
|
||||
holder.filename.text = part.name
|
||||
|
||||
val params = holder.fileBackground.layoutParams as FrameLayout.LayoutParams
|
||||
@@ -82,12 +93,16 @@ class FileBinder @Inject constructor(colors: Colors, private val context: Contex
|
||||
holder.icon.setTint(theme.textPrimary)
|
||||
holder.filename.setTextColor(theme.textPrimary)
|
||||
holder.size.setTextColor(theme.textTertiary)
|
||||
holder.share.setTint(theme.theme)
|
||||
holder.share.setBackgroundTint(theme.textPrimary)
|
||||
} else {
|
||||
holder.fileBackground.layoutParams = params.apply { gravity = Gravity.END }
|
||||
holder.fileBackground.setBackgroundTint(holder.containerView.context.resolveThemeColor(R.attr.bubbleColor))
|
||||
holder.icon.setTint(holder.containerView.context.resolveThemeColor(android.R.attr.textColorSecondary))
|
||||
holder.filename.setTextColor(holder.containerView.context.resolveThemeColor(android.R.attr.textColorPrimary))
|
||||
holder.size.setTextColor(holder.containerView.context.resolveThemeColor(android.R.attr.textColorTertiary))
|
||||
holder.share.setTint(holder.containerView.context.resolveThemeColor(R.attr.bubbleColor))
|
||||
holder.share.setBackgroundTint(holder.containerView.context.resolveThemeColor(android.R.attr.textColorTertiary))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package dev.octoshrimpy.quik.feature.gallery
|
||||
|
||||
import android.content.Context
|
||||
import com.moez.QKSMS.contentproviders.MmsPartProvider
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.Navigator
|
||||
import dev.octoshrimpy.quik.common.base.QkViewModel
|
||||
@@ -45,6 +46,9 @@ class GalleryViewModel @Inject constructor(
|
||||
private val saveImage: SaveImage,
|
||||
private val permissions: PermissionManager
|
||||
) : QkViewModel<GalleryView, GalleryState>(GalleryState()) {
|
||||
companion object {
|
||||
const val DEFAULT_SHARE_FILENAME = "quik-media-attachment.jpg"
|
||||
}
|
||||
|
||||
init {
|
||||
disposables += Flowable.just(partId)
|
||||
@@ -80,11 +84,13 @@ class GalleryViewModel @Inject constructor(
|
||||
// Share image externally
|
||||
view.optionsItemSelected()
|
||||
.filter { itemId -> itemId == R.id.share }
|
||||
.filter { permissions.hasStorage().also { if (!it) view.requestStoragePermission() } }
|
||||
.withLatestFrom(view.pageChanged()) { _, part -> part }
|
||||
.autoDisposable(view.scope())
|
||||
.subscribe { part ->
|
||||
messageRepo.savePart(part.id)?.let { navigator.shareFile(it, part.type) }
|
||||
.subscribe {
|
||||
navigator.shareFile(
|
||||
MmsPartProvider.getUriForMmsPartId(it.id, it.getBestFilename()),
|
||||
it.type
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,30 +53,29 @@
|
||||
android:progressTint="@color/tools_theme"
|
||||
android:thumbTint="@color/tools_theme" />
|
||||
|
||||
<LinearLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:clipChildren="false"
|
||||
android:orientation="horizontal">
|
||||
android:layout_marginBottom="12dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/playPause"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginStart="12dp"
|
||||
android:background="@drawable/circle"
|
||||
android:elevation="4dp"
|
||||
android:padding="6dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:srcCompat="@drawable/exo_icon_play"
|
||||
app:tint="@color/tools_theme" />
|
||||
|
||||
<dev.octoshrimpy.quik.common.widget.QkTextView
|
||||
android:id="@+id/metadataTitle"
|
||||
style="@style/TextPrimary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="12dp"
|
||||
@@ -91,8 +90,24 @@
|
||||
android:scrollHorizontally="true"
|
||||
android:singleLine="true"
|
||||
android:text="@tools:sample/lorem/random[0]"
|
||||
android:textColor="@color/black" />
|
||||
android:textColor="@color/black"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/share"
|
||||
app:layout_constraintStart_toEndOf="@id/playPause" />
|
||||
|
||||
</LinearLayout>
|
||||
<ImageView
|
||||
android:id="@+id/share"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:background="@drawable/circle"
|
||||
android:elevation="4dp"
|
||||
android:padding="6dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:srcCompat="?attr/actionModeShareDrawable"
|
||||
app:tint="@color/tools_theme" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -35,36 +35,53 @@
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:src="@drawable/ic_attachment_black_24dp"
|
||||
android:tint="?android:attr/textColorSecondary"
|
||||
app:layout_constraintBottom_toBottomOf="@id/fileBackground"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/fileBackground" />
|
||||
|
||||
<dev.octoshrimpy.quik.common.widget.QkTextView
|
||||
android:id="@+id/filename"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="12dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
app:layout_constraintStart_toEndOf="@id/icon"
|
||||
app:textSize="primary"
|
||||
tools:text="Resume.pdf" />
|
||||
app:tint="?android:attr/textColorSecondary" />
|
||||
|
||||
<dev.octoshrimpy.quik.common.widget.QkTextView
|
||||
android:id="@+id/size"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
tools:text="35 KB"
|
||||
android:paddingBottom="8dp"
|
||||
android:textColor="?android:attr/textColorTertiary"
|
||||
app:layout_constraintStart_toStartOf="@id/filename"
|
||||
app:layout_constraintTop_toBottomOf="@id/filename"
|
||||
app:textSize="secondary" />
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toStartOf="@id/share"
|
||||
app:layout_constraintStart_toEndOf="@id/icon">
|
||||
|
||||
<dev.octoshrimpy.quik.common.widget.QkTextView
|
||||
android:id="@+id/filename"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
app:textSize="primary"
|
||||
tools:text="Resume.pdf" />
|
||||
|
||||
<dev.octoshrimpy.quik.common.widget.QkTextView
|
||||
android:id="@+id/size"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:textColor="?android:attr/textColorTertiary"
|
||||
app:textSize="secondary"
|
||||
tools:text="35 KB" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/share"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:background="@drawable/circle"
|
||||
android:padding="6dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="?attr/actionModeShareDrawable"
|
||||
app:tint="@color/tools_theme" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2019 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
~
|
||||
~ This file is part of QKSMS.
|
||||
~
|
||||
~ QKSMS 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.
|
||||
~
|
||||
~ QKSMS 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 QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<paths>
|
||||
<external-path
|
||||
name="external_files"
|
||||
path="." />
|
||||
</paths>
|
||||
Reference in New Issue
Block a user