added gif entry to compose screen

This commit is contained in:
Mariotaku Lee 2017-03-29 23:15:28 +08:00
parent 912887fab7
commit c3a19fd28a
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
13 changed files with 159 additions and 101 deletions

View File

@ -56,7 +56,8 @@ subprojects {
TwitterText : '1.14.3',
MediaViewerLibrary: '0.9.23',
MultiValueSwitch : '0.9.8',
PickNCrop : '0.9.22'
PickNCrop : '0.9.22',
AndroidGIFDrawable: '1.2.6'
]
}

View File

@ -151,7 +151,7 @@ dependencies {
compile 'com.github.mariotaku:MessageBubbleView:1.6'
compile 'com.github.mariotaku:DragSortListView:0.6.1'
compile 'com.github.uucky:ColorPicker-Android:0.9.7@aar'
compile 'pl.droidsonroids.gif:android-gif-drawable:1.2.3'
compile "pl.droidsonroids.gif:android-gif-drawable:${libVersions['AndroidGIFDrawable']}"
compile 'com.sprylab.android.texturevideoview:texturevideoview:1.2.1'
compile 'com.squareup:pollexor:2.0.4'
compile 'org.apache.commons:commons-lang3:3.5'
@ -180,7 +180,6 @@ dependencies {
compile "com.github.mariotaku.MediaViewerLibrary:subsample-image-view:${libVersions['MediaViewerLibrary']}"
compile 'com.github.mariotaku:SQLiteQB:0.9.12'
compile "com.github.mariotaku.ObjectCursor:core:${libVersions['ObjectCursor']}"
compile "com.github.mariotaku:MultiValueSwitch:${libVersions['MultiValueSwitch']}"
compile 'com.github.mariotaku:AbstractTask:0.9.4'
compile "com.github.mariotaku.CommonsLibrary:parcel:${libVersions['MariotakuCommons']}"
compile "com.github.mariotaku.CommonsLibrary:io:${libVersions['MariotakuCommons']}"

View File

@ -1,25 +0,0 @@
package org.mariotaku.twidere.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import org.mariotaku.multivalueswitch.library.MultiValueSwitch;
public class ThemedMultiValueSwitch extends MultiValueSwitch {
public ThemedMultiValueSwitch(Context context) {
super(context);
}
public ThemedMultiValueSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean isShown() {
return getParent() != null && getVisibility() == View.VISIBLE;
}
}

View File

@ -51,7 +51,6 @@ import android.text.style.ImageSpan
import android.text.style.MetricAffectingSpan
import android.text.style.SuggestionSpan
import android.text.style.UpdateAppearance
import android.util.Log
import android.view.*
import android.view.View.OnClickListener
import android.view.View.OnLongClickListener
@ -204,15 +203,12 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
setupEditText()
accountSelectorButton.setOnClickListener(this)
replyLabel.setOnClickListener(this)
val attachLocation = kPreferences[attachLocationKey]
val attachPreciseLocation = kPreferences[attachPreciseLocationKey]
accountSelector.layoutManager = FixedLinearLayoutManager(this).apply {
orientation = LinearLayoutManager.HORIZONTAL
reverseLayout = false
stackFromEnd = false
}
// accountSelector.itemAnimator = DefaultItemAnimator()
accountsAdapter = AccountIconsAdapter(this).apply {
setAccounts(accountDetails)
}
@ -335,6 +331,9 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
scheduleInfo = data?.getParcelableExtra(EXTRA_SCHEDULE_INFO)
}
}
REQUEST_ADD_GIF -> {
}
}
}
@ -517,8 +516,12 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
updateTextCount()
}
R.id.schedule -> {
val controller = statusScheduleProvider ?: return true
startActivityForResult(controller.createSetScheduleIntent(), REQUEST_SET_SCHEDULE)
val provider = statusScheduleProvider ?: return true
startActivityForResult(provider.createSetScheduleIntent(), REQUEST_SET_SCHEDULE)
}
R.id.add_gif -> {
val provider = gifShareProvider ?: return true
startActivityForResult(provider.createGifSelectorIntent(), REQUEST_SET_SCHEDULE)
}
else -> {
when (item.groupId) {
@ -526,33 +529,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
locationMenuItemSelected(item)
}
else -> {
val intent = item.intent
if (intent != null) {
try {
val action = intent.action
if (INTENT_ACTION_EXTENSION_COMPOSE == action) {
val accountKeys = accountsAdapter.selectedAccountKeys
intent.putExtra(EXTRA_TEXT, ParseUtils.parseString(editText.text))
intent.putExtra(EXTRA_ACCOUNT_KEYS, accountKeys)
if (accountKeys.isNotEmpty()) {
val accountKey = accountKeys.first()
intent.putExtra(EXTRA_NAME, DataStoreUtils.getAccountName(this, accountKey))
intent.putExtra(EXTRA_SCREEN_NAME, DataStoreUtils.getAccountScreenName(this, accountKey))
}
inReplyToStatus?.let {
intent.putExtra(EXTRA_IN_REPLY_TO_ID, it.id)
intent.putExtra(EXTRA_IN_REPLY_TO_NAME, it.user_name)
intent.putExtra(EXTRA_IN_REPLY_TO_SCREEN_NAME, it.user_screen_name)
}
startActivityForResult(intent, REQUEST_EXTENSION_COMPOSE)
} else {
startActivity(intent)
}
} catch (e: ActivityNotFoundException) {
Log.w(LOGTAG, e)
return false
}
}
extensionIntentItemSelected(item)
}
}
}
@ -622,7 +599,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
}
val values = ObjectCursor.valuesCreatorFrom(Draft::class.java).create(draft)
val draftUri = contentResolver.insert(Drafts.CONTENT_URI, values)
displayNewDraftNotification(text, draftUri)
displayNewDraftNotification(draftUri)
return draftUri
}
@ -711,6 +688,33 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
updateTextCount()
}
private fun extensionIntentItemSelected(item: MenuItem) {
val intent = item.intent ?: return
try {
val action = intent.action
if (INTENT_ACTION_EXTENSION_COMPOSE == action) {
val accountKeys = accountsAdapter.selectedAccountKeys
intent.putExtra(EXTRA_TEXT, ParseUtils.parseString(editText.text))
intent.putExtra(EXTRA_ACCOUNT_KEYS, accountKeys)
if (accountKeys.isNotEmpty()) {
val accountKey = accountKeys.first()
intent.putExtra(EXTRA_NAME, DataStoreUtils.getAccountName(this, accountKey))
intent.putExtra(EXTRA_SCREEN_NAME, DataStoreUtils.getAccountScreenName(this, accountKey))
}
inReplyToStatus?.let {
intent.putExtra(EXTRA_IN_REPLY_TO_ID, it.id)
intent.putExtra(EXTRA_IN_REPLY_TO_NAME, it.user_name)
intent.putExtra(EXTRA_IN_REPLY_TO_SCREEN_NAME, it.user_screen_name)
}
startActivityForResult(intent, REQUEST_EXTENSION_COMPOSE)
} else {
startActivity(intent)
}
} catch (e: ActivityNotFoundException) {
Analyzer.logException(e)
}
}
private fun updateViewStyle() {
accountProfileImage.style = preferences[profileImageStyleKey]
}
@ -853,7 +857,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
setMenu()
}
private fun displayNewDraftNotification(text: String, draftUri: Uri) {
private fun displayNewDraftNotification(draftUri: Uri) {
val values = ContentValues()
values.put(BaseColumns._ID, draftUri.lastPathSegment)
contentResolver.insert(Drafts.CONTENT_URI_NOTIFICATIONS, values)
@ -1249,9 +1253,11 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
* Has media & Not reply: [Take photo][Media menu][Attach location][Drafts]
* Is reply: [Media menu][View status][Attach location][Drafts]
*/
MenuUtils.setItemAvailability(menu, R.id.toggle_sensitive, hasMedia)
MenuUtils.setItemAvailability(menu, R.id.schedule, extraFeaturesService.isSupported(
menu.setItemAvailability(R.id.toggle_sensitive, hasMedia)
menu.setItemAvailability(R.id.schedule, extraFeaturesService.isSupported(
ExtraFeaturesService.FEATURE_SCHEDULE_STATUS))
menu.setItemAvailability(R.id.add_gif, extraFeaturesService.isSupported(
ExtraFeaturesService.FEATURE_SHARE_GIF))
menu.setGroupEnabled(MENU_GROUP_IMAGE_EXTENSION, hasMedia)
menu.setGroupVisible(MENU_GROUP_IMAGE_EXTENSION, hasMedia)
@ -1576,7 +1582,6 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
) : BaseRecyclerViewAdapter<AccountIconViewHolder>(activity, Glide.with(activity)) {
private val inflater: LayoutInflater = activity.layoutInflater
private val selection: MutableMap<UserKey, Boolean> = HashMap()
val isNameFirst: Boolean = preferences[nameFirstKey]
private var accounts: Array<AccountDetails>? = null
@ -1926,17 +1931,12 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
private const val EXTRA_DRAFT_UNIQUE_ID = "draft_unique_id"
private const val DISCARD_STATUS_DIALOG_FRAGMENT_TAG = "discard_status"
val LOCATION_VALUE_PLACE = "place"
val LOCATION_VALUE_COORDINATE = "coordinate"
val LOCATION_VALUE_NONE = "none"
private val LOCATION_OPTIONS = arrayOf(LOCATION_VALUE_NONE, LOCATION_VALUE_PLACE, LOCATION_VALUE_COORDINATE)
private const val REQUEST_ATTACH_LOCATION_PERMISSION = 301
private const val REQUEST_PICK_MEDIA_PERMISSION = 302
private const val REQUEST_TAKE_PHOTO_PERMISSION = 303
private const val REQUEST_CAPTURE_VIDEO_PERMISSION = 304
private const val REQUEST_SET_SCHEDULE = 304
private const val REQUEST_SET_SCHEDULE = 305
private const val REQUEST_ADD_GIF = 306
internal fun getDraftAction(intentAction: String?): String {
if (intentAction == null) {

View File

@ -0,0 +1,22 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.model.media
data class NoThumborUrl(val url: String)

View File

@ -35,7 +35,7 @@ import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.util.media.TwidereMediaDownloader
import java.io.InputStream
class AuthenticatedUrlLoader(
class AuthenticatedUriLoader(
val context: Context,
val client: OkHttpClient
) : ModelLoader<AuthenticatedUri, InputStream> {
@ -43,10 +43,8 @@ class AuthenticatedUrlLoader(
override fun getResourceFetcher(model: AuthenticatedUri, width: Int, height: Int): DataFetcher<InputStream> {
val headersBuilder = LazyHeaders.Builder()
val credentials = model.accountKey?.credentials
if (credentials != null) {
if (TwidereMediaDownloader.isAuthRequired(credentials, model.uri)) {
headersBuilder.addHeader("Authorization", AuthorizationHeaderFactory(model.uri, credentials))
}
if (credentials != null && TwidereMediaDownloader.isAuthRequired(credentials, model.uri)) {
headersBuilder.addHeader("Authorization", AuthorizationHeaderFactory(model.uri, credentials))
}
val glideUrl = GlideUrl(model.uri.toString(), headersBuilder.build())
return OkHttpStreamFetcher(client, glideUrl)
@ -62,7 +60,7 @@ class AuthenticatedUrlLoader(
}
class Factory(val client: OkHttpClient) : ModelLoaderFactory<AuthenticatedUri, InputStream> {
override fun build(context: Context, factories: GenericLoaderFactory) = AuthenticatedUrlLoader(context, client)
override fun build(context: Context, factories: GenericLoaderFactory) = AuthenticatedUriLoader(context, client)
override fun teardown() {}
}

View File

@ -0,0 +1,52 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.util.glide
import android.content.Context
import com.bumptech.glide.integration.okhttp3.OkHttpStreamFetcher
import com.bumptech.glide.load.data.DataFetcher
import com.bumptech.glide.load.model.*
import okhttp3.OkHttpClient
import org.mariotaku.twidere.model.media.NoThumborUrl
import java.io.InputStream
class NoThumborUrlLoader(
val context: Context,
val client: OkHttpClient
) : ModelLoader<NoThumborUrl, InputStream> {
override fun getResourceFetcher(model: NoThumborUrl, width: Int, height: Int): DataFetcher<InputStream> {
val headersBuilder = LazyHeaders.Builder()
headersBuilder.addHeader(HEADER_NO_THUMBOR, "true")
val glideUrl = GlideUrl(model.url, headersBuilder.build())
return OkHttpStreamFetcher(client, glideUrl)
}
class Factory(val client: OkHttpClient) : ModelLoaderFactory<NoThumborUrl, InputStream> {
override fun build(context: Context, factories: GenericLoaderFactory) = NoThumborUrlLoader(context, client)
override fun teardown() {}
}
companion object {
const val HEADER_NO_THUMBOR = "X-Twidere-No-Thumbor"
}
}

View File

@ -29,9 +29,11 @@ import com.bumptech.glide.module.GlideModule
import okhttp3.OkHttpClient
import okhttp3.Request
import org.mariotaku.twidere.model.media.AuthenticatedUri
import org.mariotaku.twidere.model.media.NoThumborUrl
import org.mariotaku.twidere.util.HttpClientFactory
import org.mariotaku.twidere.util.UserAgentUtils
import org.mariotaku.twidere.util.dagger.DependencyHolder
import org.mariotaku.twidere.util.glide.NoThumborUrlLoader.Companion.HEADER_NO_THUMBOR
import org.mariotaku.twidere.util.media.ThumborWrapper
import org.mariotaku.twidere.util.okhttp.ModifyRequestInterceptor
import java.io.InputStream
@ -51,16 +53,22 @@ class TwidereGlideModule : GlideModule {
builder.addInterceptor(ModifyRequestInterceptor(ThumborModifier(thumbor), UserAgentModifier(userAgent)))
val client = builder.build()
glide.register(GlideUrl::class.java, InputStream::class.java, OkHttpUrlLoader.Factory(client))
glide.register(AuthenticatedUri::class.java, InputStream::class.java, AuthenticatedUrlLoader.Factory(client))
glide.register(AuthenticatedUri::class.java, InputStream::class.java, AuthenticatedUriLoader.Factory(client))
glide.register(NoThumborUrl::class.java, InputStream::class.java, NoThumborUrlLoader.Factory(client))
}
class ThumborModifier(val thumbor: ThumborWrapper) : ModifyRequestInterceptor.RequestModifier {
override fun modify(original: Request, builder: Request.Builder): Boolean {
if (!thumbor.available) return false
// Since Thumbor doesn't support Authorization header, disable for requests with authorization
if (original.header("Authorization") != null) {
return false
}
if (original.header(HEADER_NO_THUMBOR) != null) {
builder.removeHeader(HEADER_NO_THUMBOR)
return false
}
builder.url(thumbor.buildUri(original.url().toString()))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
builder.header("Accept", "image/webp, */*")
@ -71,6 +79,7 @@ class TwidereGlideModule : GlideModule {
}
class UserAgentModifier(val userAgent: String) : ModifyRequestInterceptor.RequestModifier {
override fun modify(original: Request, builder: Request.Builder): Boolean {
builder.header("User-Agent", userAgent)
return true

View File

@ -52,6 +52,7 @@ abstract class ExtraFeaturesService {
const val FEATURE_FILTERS_SUBSCRIPTION = "filters_subscriptions"
const val FEATURE_SYNC_DATA = "sync_data"
const val FEATURE_SCHEDULE_STATUS = "schedule_status"
const val FEATURE_SHARE_GIF = "share_gif"
fun newInstance(context: Context): ExtraFeaturesService {
val instance = ServiceLoader.load(ExtraFeaturesService::class.java).firstOrNull() ?: run {
@ -72,6 +73,8 @@ abstract class ExtraFeaturesService {
context.getString(R.string.extra_feature_description_filters_subscription))
FEATURE_SCHEDULE_STATUS -> Introduction(R.drawable.ic_action_time,
context.getString(R.string.extra_feature_description_schedule_status))
FEATURE_SHARE_GIF -> Introduction(R.drawable.ic_action_gif,
context.getString(R.string.extra_feature_description_share_gif))
else -> throw UnsupportedOperationException(feature)
}
}

View File

@ -1,16 +1,13 @@
package org.mariotaku.twidere.util.theme
import android.content.Context
import android.support.annotation.ColorInt
import android.util.AttributeSet
import android.view.View
import com.pnikosis.materialishprogress.ProgressWheel
import com.rengwuxian.materialedittext.MaterialEditText
import org.mariotaku.chameleon.Chameleon
import org.mariotaku.chameleon.ChameleonView
import org.mariotaku.chameleon.view.ChameleonSwitchCompat
import org.mariotaku.chameleon.view.ChameleonTextView
import org.mariotaku.multivalueswitch.library.MultiValueSwitch
object TwidereAppearanceCreator : Chameleon.AppearanceCreator {
override fun createAppearance(view: View, context: Context, attributeSet: AttributeSet, theme: Chameleon.Theme): ChameleonView.Appearance? {
@ -18,9 +15,6 @@ object TwidereAppearanceCreator : Chameleon.AppearanceCreator {
is ProgressWheel -> {
return BasicColorAppearance(theme.colorAccent)
}
is MultiValueSwitch -> {
return ChameleonSwitchCompat.Appearance.create(theme)
}
is MaterialEditText -> {
return ChameleonTextView.Appearance.create(view, context, attributeSet, theme)
}
@ -34,10 +28,6 @@ object TwidereAppearanceCreator : Chameleon.AppearanceCreator {
appearance as BasicColorAppearance
view.barColor = appearance.color
}
is MultiValueSwitch -> {
appearance as ChameleonSwitchCompat.Appearance
setMultiValueSwitchTint(view, appearance.accentColor, appearance.isDark)
}
is MaterialEditText -> {
appearance as ChameleonTextView.Appearance
ChameleonTextView.Appearance.apply(view, appearance)
@ -49,15 +39,4 @@ object TwidereAppearanceCreator : Chameleon.AppearanceCreator {
data class BasicColorAppearance(var color: Int) : ChameleonView.Appearance
fun setMultiValueSwitchTint(switchView: MultiValueSwitch, @ColorInt color: Int, useDarker: Boolean) {
if (switchView.trackDrawable != null) {
switchView.trackDrawable = ChameleonSwitchCompat.modifySwitchDrawable(switchView.context,
switchView.trackDrawable, color, false, true, useDarker)
}
if (switchView.thumbDrawable != null) {
switchView.thumbDrawable = ChameleonSwitchCompat.modifySwitchDrawable(switchView.context,
switchView.thumbDrawable, color, true, true, useDarker)
}
}
}

View File

@ -24,6 +24,10 @@
android:id="@+id/record_video"
android:icon="@drawable/ic_action_camcorder"
android:title="@string/action_record_video"/>
<item
android:id="@+id/add_gif"
android:icon="@drawable/ic_action_gif"
android:title="@string/action_add_gif"/>
<item
android:id="@id/toggle_sensitive"
android:checkable="true"

View File

@ -18,6 +18,7 @@
<string name="action_add_filter_rule">Add rule</string>
<string name="action_add_filters_subscription">Add subscription</string>
<string name="action_add_media">Add media</string>
<string name="action_add_gif">Add GIF</string>
<string name="action_add_member">Add member</string>
<string name="action_add_tab">Add tab</string>
<string name="action_add_to_filter">Add to filter</string>
@ -397,6 +398,7 @@
<string name="extra_feature_description_filters_import">Import filter list from blocks/mutes</string>
<string name="extra_feature_description_filters_subscription">Subscribe to mute filters and sync automatically</string>
<string name="extra_feature_description_schedule_status">Schedule tweet (send later)</string>
<string name="extra_feature_description_share_gif">Share GIF in tweet</string>
<string name="extra_feature_description_sync_data">Sync data with Dropbox, Google Drive etc</string>
<string name="extra_feature_title_filters_import">Filters import</string>
<string name="extra_feature_title_filters_subscription">Filters subscription</string>
@ -1284,4 +1286,5 @@
<string name="users_blocked">Blocked these users.</string>
<string name="users_lists_with_name"><xliff:g id="name">%s</xliff:g>\'s lists</string>
<string name="users_statuses">User\'s tweets</string>
<string name="title_giphy_search">Search GIF by Giphy</string>
</resources>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 42 (36781) - http://www.bohemiancoding.com/sketch -->
<title>ic_action_gif-mdpi</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Action-Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="ic_action_gif-mdpi">
<path d="M4,4 L28,4 L28,28 L4,28 L4,4 Z M15.75,15.47 L15.64,15.36 L15.75,15.47 Z" id="Shape"></path>
<path d="M5,9.99406028 C5,8.8927712 5.88967395,8 6.991155,8 L25.008845,8 C26.1085295,8 27,8.89451376 27,9.99406028 L27,22.0059397 C27,23.1072288 26.1103261,24 25.008845,24 L6.991155,24 C5.89147046,24 5,23.1054862 5,22.0059397 L5,9.99406028 Z M14.0878477,18.6207906 L14.0878477,15.6434846 L11.1515373,15.6434846 L11.1515373,16.7759882 L12.5505124,16.7759882 L12.5505124,18.0622254 C12.3045375,18.3082003 11.8928774,18.4311859 11.3155197,18.4311859 C10.7415784,18.4311859 10.3060044,18.2313343 10.0087848,17.8316251 C9.71156512,17.431916 9.56295753,16.8409019 9.56295753,16.0585651 L9.56295753,15.5819912 C9.56637384,14.8064871 9.70473263,14.2223055 9.97803805,13.829429 C10.2513435,13.4365524 10.6493385,13.2401171 11.1720351,13.2401171 C11.5819932,13.2401171 11.9039763,13.3391888 12.1379941,13.5373353 C12.3720119,13.7354817 12.5231817,14.0480701 12.591508,14.4751098 L14.0878477,14.4751098 C13.9956071,13.6688588 13.7001001,13.0547849 13.2013177,12.6328697 C12.7025353,12.2109545 12.0107413,12 11.1259151,12 C10.49048,12 9.93619073,12.1443373 9.46303073,12.4330161 C8.98987073,12.7216949 8.62774649,13.1384795 8.37664714,13.6833821 C8.12554779,14.2282848 8,14.8731051 8,15.6178623 L8,16.125183 C8.00683264,16.8494423 8.14262923,17.4797435 8.40739385,18.0161054 C8.67215847,18.5524672 9.04794778,18.9615651 9.53477305,19.2434114 C10.0215983,19.5252576 10.5878445,19.6661786 11.2335285,19.6661786 C11.8484657,19.6661786 12.4087334,19.5747934 12.9143484,19.3920204 C13.4199634,19.2092474 13.8111259,18.9521734 14.0878477,18.6207906 Z M17.3426061,19.5636895 L17.3426061,12.102489 L15.8052709,12.102489 L15.8052709,19.5636895 L17.3426061,19.5636895 Z M23.6310395,16.5146412 L23.6310395,15.2745241 L20.6793558,15.2745241 L20.6793558,13.3477306 L24,13.3477306 L24,12.102489 L19.1420205,12.102489 L19.1420205,19.5636895 L20.6793558,19.5636895 L20.6793558,16.5146412 L23.6310395,16.5146412 Z" id="Combined-Shape" fill="#FFFFFF"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB