mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-25 19:05:56 +03:00
Merge pull request #1345 from emmaguy/direct-shortcuts
Add direct shortcuts
This commit is contained in:
commit
7f185a729e
5 changed files with 148 additions and 10 deletions
|
@ -2,7 +2,7 @@ Changes in RiotX 0.20.0 (2020-XX-XX)
|
||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
Features ✨:
|
Features ✨:
|
||||||
-
|
- Add Direct Shortcuts (#652)
|
||||||
|
|
||||||
Improvements 🙌:
|
Improvements 🙌:
|
||||||
- Improve notification accessibility with ticker text (#1226)
|
- Improve notification accessibility with ticker text (#1226)
|
||||||
|
|
|
@ -17,11 +17,13 @@
|
||||||
package im.vector.riotx.features.home
|
package im.vector.riotx.features.home
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import androidx.annotation.AnyThread
|
import androidx.annotation.AnyThread
|
||||||
import androidx.annotation.UiThread
|
import androidx.annotation.UiThread
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
import com.amulyakhare.textdrawable.TextDrawable
|
import com.amulyakhare.textdrawable.TextDrawable
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.bumptech.glide.request.target.DrawableImageViewTarget
|
import com.bumptech.glide.request.target.DrawableImageViewTarget
|
||||||
|
@ -72,6 +74,28 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active
|
||||||
.into(target)
|
.into(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@AnyThread
|
||||||
|
fun shortcutDrawable(context: Context, glideRequest: GlideRequests, matrixItem: MatrixItem, iconSize: Int): Bitmap {
|
||||||
|
return glideRequest
|
||||||
|
.asBitmap()
|
||||||
|
.apply {
|
||||||
|
val resolvedUrl = resolvedUrl(matrixItem.avatarUrl)
|
||||||
|
if (resolvedUrl != null) {
|
||||||
|
load(resolvedUrl)
|
||||||
|
} else {
|
||||||
|
val avatarColor = avatarColor(matrixItem, context)
|
||||||
|
load(TextDrawable.builder()
|
||||||
|
.beginConfig()
|
||||||
|
.bold()
|
||||||
|
.endConfig()
|
||||||
|
.buildRect(matrixItem.firstLetterOfDisplayName(), avatarColor)
|
||||||
|
.toBitmap(width = iconSize, height = iconSize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.submit(iconSize, iconSize)
|
||||||
|
.get()
|
||||||
|
}
|
||||||
|
|
||||||
@AnyThread
|
@AnyThread
|
||||||
fun getCachedDrawable(glideRequest: GlideRequests, matrixItem: MatrixItem): Drawable {
|
fun getCachedDrawable(glideRequest: GlideRequests, matrixItem: MatrixItem): Drawable {
|
||||||
return buildGlideRequest(glideRequest, matrixItem.avatarUrl)
|
return buildGlideRequest(glideRequest, matrixItem.avatarUrl)
|
||||||
|
@ -82,10 +106,7 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active
|
||||||
|
|
||||||
@AnyThread
|
@AnyThread
|
||||||
fun getPlaceholderDrawable(context: Context, matrixItem: MatrixItem): Drawable {
|
fun getPlaceholderDrawable(context: Context, matrixItem: MatrixItem): Drawable {
|
||||||
val avatarColor = when (matrixItem) {
|
val avatarColor = avatarColor(matrixItem, context)
|
||||||
is MatrixItem.UserItem -> ContextCompat.getColor(context, getColorFromUserId(matrixItem.id))
|
|
||||||
else -> ContextCompat.getColor(context, getColorFromRoomId(matrixItem.id))
|
|
||||||
}
|
|
||||||
return TextDrawable.builder()
|
return TextDrawable.builder()
|
||||||
.beginConfig()
|
.beginConfig()
|
||||||
.bold()
|
.bold()
|
||||||
|
@ -96,11 +117,21 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active
|
||||||
// PRIVATE API *********************************************************************************
|
// PRIVATE API *********************************************************************************
|
||||||
|
|
||||||
private fun buildGlideRequest(glideRequest: GlideRequests, avatarUrl: String?): GlideRequest<Drawable> {
|
private fun buildGlideRequest(glideRequest: GlideRequests, avatarUrl: String?): GlideRequest<Drawable> {
|
||||||
val resolvedUrl = activeSessionHolder.getSafeActiveSession()?.contentUrlResolver()
|
val resolvedUrl = resolvedUrl(avatarUrl)
|
||||||
?.resolveThumbnail(avatarUrl, THUMBNAIL_SIZE, THUMBNAIL_SIZE, ContentUrlResolver.ThumbnailMethod.SCALE)
|
|
||||||
|
|
||||||
return glideRequest
|
return glideRequest
|
||||||
.load(resolvedUrl)
|
.load(resolvedUrl)
|
||||||
.apply(RequestOptions.circleCropTransform())
|
.apply(RequestOptions.circleCropTransform())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun resolvedUrl(avatarUrl: String?): String? {
|
||||||
|
return activeSessionHolder.getSafeActiveSession()?.contentUrlResolver()
|
||||||
|
?.resolveThumbnail(avatarUrl, THUMBNAIL_SIZE, THUMBNAIL_SIZE, ContentUrlResolver.ThumbnailMethod.SCALE)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun avatarColor(matrixItem: MatrixItem, context: Context): Int {
|
||||||
|
return when (matrixItem) {
|
||||||
|
is MatrixItem.UserItem -> ContextCompat.getColor(context, getColorFromUserId(matrixItem.id))
|
||||||
|
else -> ContextCompat.getColor(context, getColorFromRoomId(matrixItem.id))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
|
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
|
||||||
@Inject lateinit var vectorPreferences: VectorPreferences
|
@Inject lateinit var vectorPreferences: VectorPreferences
|
||||||
@Inject lateinit var popupAlertManager: PopupAlertManager
|
@Inject lateinit var popupAlertManager: PopupAlertManager
|
||||||
|
@Inject lateinit var shortcutsHandler: ShortcutsHandler
|
||||||
|
|
||||||
private val drawerListener = object : DrawerLayout.SimpleDrawerListener() {
|
private val drawerListener = object : DrawerLayout.SimpleDrawerListener() {
|
||||||
override fun onDrawerStateChanged(newState: Int) {
|
override fun onDrawerStateChanged(newState: Int) {
|
||||||
|
@ -144,6 +145,9 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
&& activeSessionHolder.getSafeActiveSession()?.hasAlreadySynced() == true) {
|
&& activeSessionHolder.getSafeActiveSession()?.hasAlreadySynced() == true) {
|
||||||
promptCompleteSecurityIfNeeded()
|
promptCompleteSecurityIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shortcutsHandler.observeRoomsAndBuildShortcuts()
|
||||||
|
.disposeOnDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun promptCompleteSecurityIfNeeded() {
|
private fun promptCompleteSecurityIfNeeded() {
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.riotx.features.home
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.core.content.pm.ShortcutInfoCompat
|
||||||
|
import androidx.core.content.pm.ShortcutManagerCompat
|
||||||
|
import androidx.core.graphics.drawable.IconCompat
|
||||||
|
import im.vector.matrix.android.api.session.room.model.tag.RoomTag
|
||||||
|
import im.vector.matrix.android.api.util.toMatrixItem
|
||||||
|
import im.vector.riotx.core.glide.GlideApp
|
||||||
|
import im.vector.riotx.core.utils.DimensionConverter
|
||||||
|
import im.vector.riotx.features.home.room.detail.RoomDetailActivity
|
||||||
|
import io.reactivex.disposables.Disposable
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private val useAdaptiveIcon = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
||||||
|
private const val adaptiveIconSizeDp = 108
|
||||||
|
private const val adaptiveIconOuterSidesDp = 18
|
||||||
|
|
||||||
|
class ShortcutsHandler @Inject constructor(
|
||||||
|
private val context: Context,
|
||||||
|
private val homeRoomListStore: HomeRoomListDataSource,
|
||||||
|
private val avatarRenderer: AvatarRenderer,
|
||||||
|
private val dimensionConverter: DimensionConverter
|
||||||
|
) {
|
||||||
|
private val adaptiveIconSize = dimensionConverter.dpToPx(adaptiveIconSizeDp)
|
||||||
|
private val adaptiveIconOuterSides = dimensionConverter.dpToPx(adaptiveIconOuterSidesDp)
|
||||||
|
private val iconSize by lazy {
|
||||||
|
if (useAdaptiveIcon) {
|
||||||
|
adaptiveIconSize - adaptiveIconOuterSides
|
||||||
|
} else {
|
||||||
|
dimensionConverter.dpToPx(72)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun observeRoomsAndBuildShortcuts(): Disposable {
|
||||||
|
return homeRoomListStore
|
||||||
|
.observe()
|
||||||
|
.distinct()
|
||||||
|
.observeOn(Schedulers.computation())
|
||||||
|
.subscribe { rooms ->
|
||||||
|
val shortcuts = rooms
|
||||||
|
.filter { room -> room.tags.any { it.name == RoomTag.ROOM_TAG_FAVOURITE } }
|
||||||
|
.take(n = 4) // Android only allows us to create 4 shortcuts
|
||||||
|
.map { room ->
|
||||||
|
val intent = RoomDetailActivity.shortcutIntent(context, room.roomId)
|
||||||
|
val bitmap = avatarRenderer.shortcutDrawable(context, GlideApp.with(context), room.toMatrixItem(), iconSize)
|
||||||
|
|
||||||
|
ShortcutInfoCompat.Builder(context, room.roomId)
|
||||||
|
.setShortLabel(room.displayName)
|
||||||
|
.setIcon(bitmap.toProfileImageIcon())
|
||||||
|
.setIntent(intent)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
ShortcutManagerCompat.removeAllDynamicShortcuts(context)
|
||||||
|
ShortcutManagerCompat.addDynamicShortcuts(context, shortcuts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRIVATE API *********************************************************************************
|
||||||
|
|
||||||
|
private fun Bitmap.toProfileImageIcon(): IconCompat {
|
||||||
|
return if (useAdaptiveIcon) {
|
||||||
|
IconCompat.createWithAdaptiveBitmap(this)
|
||||||
|
} else {
|
||||||
|
IconCompat.createWithBitmap(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,8 +44,14 @@ class RoomDetailActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
waitingView = waiting_view
|
waitingView = waiting_view
|
||||||
if (isFirstCreation()) {
|
if (isFirstCreation()) {
|
||||||
val roomDetailArgs: RoomDetailArgs = intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS)
|
val roomDetailArgs: RoomDetailArgs? = if (intent?.action == ACTION_ROOM_DETAILS_FROM_SHORTCUT) {
|
||||||
?: return
|
RoomDetailArgs(roomId = intent?.extras?.getString(EXTRA_ROOM_ID)!!)
|
||||||
|
} else {
|
||||||
|
intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (roomDetailArgs == null) return
|
||||||
|
|
||||||
currentRoomId = roomDetailArgs.roomId
|
currentRoomId = roomDetailArgs.roomId
|
||||||
replaceFragment(R.id.roomDetailContainer, RoomDetailFragment::class.java, roomDetailArgs)
|
replaceFragment(R.id.roomDetailContainer, RoomDetailFragment::class.java, roomDetailArgs)
|
||||||
replaceFragment(R.id.roomDetailDrawerContainer, BreadcrumbsFragment::class.java)
|
replaceFragment(R.id.roomDetailDrawerContainer, BreadcrumbsFragment::class.java)
|
||||||
|
@ -110,11 +116,20 @@ class RoomDetailActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
const val EXTRA_ROOM_DETAIL_ARGS = "EXTRA_ROOM_DETAIL_ARGS"
|
const val EXTRA_ROOM_DETAIL_ARGS = "EXTRA_ROOM_DETAIL_ARGS"
|
||||||
|
const val EXTRA_ROOM_ID = "EXTRA_ROOM_ID"
|
||||||
|
const val ACTION_ROOM_DETAILS_FROM_SHORTCUT = "ROOM_DETAILS_FROM_SHORTCUT"
|
||||||
|
|
||||||
fun newIntent(context: Context, roomDetailArgs: RoomDetailArgs): Intent {
|
fun newIntent(context: Context, roomDetailArgs: RoomDetailArgs): Intent {
|
||||||
return Intent(context, RoomDetailActivity::class.java).apply {
|
return Intent(context, RoomDetailActivity::class.java).apply {
|
||||||
putExtra(EXTRA_ROOM_DETAIL_ARGS, roomDetailArgs)
|
putExtra(EXTRA_ROOM_DETAIL_ARGS, roomDetailArgs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun shortcutIntent(context: Context, roomId: String): Intent {
|
||||||
|
return Intent(context, RoomDetailActivity::class.java).apply {
|
||||||
|
action = ACTION_ROOM_DETAILS_FROM_SHORTCUT
|
||||||
|
putExtra(EXTRA_ROOM_ID, roomId)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue