distinguished incoming and outgoing message with color

supported notification channel settings on Android 8.0
This commit is contained in:
Mariotaku Lee 2017-08-30 20:17:55 +08:00
parent b4224b099d
commit 20d014e370
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
12 changed files with 248 additions and 95 deletions

168
gradlew.bat vendored
View File

@ -1,84 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -20,14 +20,19 @@
package org.mariotaku.twidere.adapter
import android.content.Context
import android.content.res.ColorStateList
import android.support.v4.graphics.ColorUtils
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.bumptech.glide.RequestManager
import org.apache.commons.lang3.time.DateUtils
import org.mariotaku.chameleon.Chameleon
import org.mariotaku.chameleon.ChameleonUtils
import org.mariotaku.kpreferences.get
import org.mariotaku.library.objectcursor.ObjectCursor
import org.mariotaku.twidere.R
import org.mariotaku.twidere.adapter.iface.IItemCountsAdapter
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter
import org.mariotaku.twidere.annotation.PreviewStyle
@ -40,6 +45,7 @@ import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.ParcelableMessage.MessageType
import org.mariotaku.twidere.provider.TwidereDataStore.Messages
import org.mariotaku.twidere.util.DirectMessageOnLinkClickHandler
import org.mariotaku.twidere.util.ThemeUtils
import org.mariotaku.twidere.util.TwidereLinkify
import org.mariotaku.twidere.view.CardMediaContainer.OnMediaClickListener
import org.mariotaku.twidere.view.holder.LoadIndicatorViewHolder
@ -78,6 +84,9 @@ class MessagesConversationAdapter(
var listener: Listener? = null
var displaySenderProfile: Boolean = false
val bubbleColorOutgoing: ColorStateList? = ThemeUtils.getColorStateListFromAttribute(context, R.attr.messageBubbleColor)
val bubbleColorIncoming: ColorStateList? = context.getIncomingMessageColor()
override var loadMoreIndicatorPosition: Long
get() = super.loadMoreIndicatorPosition
set(value) {
@ -213,6 +222,19 @@ class MessagesConversationAdapter(
private const val ITEM_TYPE_STICKER_MESSAGE = 2
private const val ITEM_TYPE_NOTICE_MESSAGE = 3
private const val ITEM_LOAD_OLDER_INDICATOR = 4
private fun Context.getIncomingMessageColor(): ColorStateList {
val foregroundColor = ThemeUtils.getColorForeground(this)
val themeColor = Chameleon.getOverrideTheme(this, ChameleonUtils.getActivity(this)).colorAccent
val normalColor = ThemeUtils.getOptimalAccentColor(themeColor, foregroundColor)
val pressedColor = if (ColorUtils.calculateLuminance(normalColor) < 0.1) {
ColorUtils.compositeColors(0x20FFFFFF, normalColor)
} else {
ColorUtils.compositeColors(0x20000000, normalColor)
}
return ColorStateList(arrayOf(intArrayOf(android.R.attr.state_pressed), intArrayOf(0)),
intArrayOf(pressedColor, normalColor))
}
}

View File

@ -40,4 +40,13 @@ fun UserKey.notificationChannelId(id: String): String {
fun UserKey.notificationChannelGroupId(): String {
return Uri.encode(toString())
}
fun NotificationChannelSpec.getName(context: Context): String {
return context.getString(nameRes)
}
fun NotificationChannelSpec.getDescription(context: Context): String? {
if (descriptionRes == 0) return null
return context.getString(descriptionRes)
}

View File

@ -22,6 +22,7 @@ package org.mariotaku.twidere.fragment
import android.os.Bundle
import org.mariotaku.twidere.R
import org.mariotaku.twidere.constant.SharedPreferenceConstants.*
import org.mariotaku.twidere.preference.notification.AccountNotificationChannelsPreference
class AccountNotificationSettingsFragment : BaseAccountPreferenceFragment() {
@ -36,11 +37,13 @@ class AccountNotificationSettingsFragment : BaseAccountPreferenceFragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
val preference = findPreference(KEY_NOTIFICATION_LIGHT_COLOR)
val account = account
if (preference != null && account != null) {
preference.setDefaultValue(account.color)
val account = this.account
findPreference(KEY_NOTIFICATION_LIGHT_COLOR)?.let {
if (account != null) {
it.setDefaultValue(account.color)
}
}
(findPreference("notification_channels") as? AccountNotificationChannelsPreference)?.account = account
}
}

View File

@ -59,12 +59,14 @@ enum class NotificationChannelSpec(
* Such as new statuses posted by friends.
*/
contentUpdates("content_updates", R.string.notification_channel_name_content_updates,
descriptionRes = R.string.notification_channel_descriptions_content_updates,
importance = NotificationManager.IMPORTANCE_DEFAULT, showBadge = true, grouped = true),
/**
* For updates related to micro-blogging features.
* Such as new statuses posted by friends user subscribed to.
*/
contentSubscriptions("content_subscriptions", R.string.notification_channel_name_content_subscriptions,
descriptionRes = R.string.notification_channel_descriptions_content_subscriptions,
importance = NotificationManager.IMPORTANCE_HIGH, showBadge = true, grouped = true),
/**
* For interactions related to micro-blogging features.

View File

@ -20,6 +20,7 @@
package org.mariotaku.twidere.preference
import android.accounts.AccountManager
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
@ -93,7 +94,7 @@ abstract class AccountsListPreference(context: Context, attrs: AttributeSet? = n
switchPreference = context.getSharedPreferences(switchPreferenceName, Context.MODE_PRIVATE)
switchPreference.registerOnSharedPreferenceChangeListener(this)
title = account.user.name
summary = String.format("@%s", account.user.screen_name)
summary = "@${account.user.screen_name}"
widgetLayoutResource = R.layout.layout_preference_switch_indicator
}
@ -102,17 +103,17 @@ abstract class AccountsListPreference(context: Context, attrs: AttributeSet? = n
}
@SuppressLint("RestrictedApi")
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
val iconView = holder.findViewById(android.R.id.icon)
if (iconView is PreferenceImageView) {
val imageView = iconView
val maxSize = context.resources.getDimensionPixelSize(R.dimen.element_size_normal)
imageView.minimumWidth = maxSize
imageView.minimumHeight = maxSize
imageView.maxWidth = maxSize
imageView.maxHeight = maxSize
imageView.scaleType = ImageView.ScaleType.CENTER_CROP
iconView.minimumWidth = maxSize
iconView.minimumHeight = maxSize
iconView.maxWidth = maxSize
iconView.maxHeight = maxSize
iconView.scaleType = ImageView.ScaleType.CENTER_CROP
}
val titleView = holder.findViewById(android.R.id.title)
if (titleView is TextView) {

View File

@ -0,0 +1,66 @@
/*
* 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.preference.notification
import android.annotation.TargetApi
import android.content.Context
import android.content.Intent
import android.os.Build
import android.provider.Settings
import android.support.v7.preference.Preference
import android.support.v7.preference.PreferenceManager
import android.util.AttributeSet
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.extension.model.getDescription
import org.mariotaku.twidere.extension.model.getName
import org.mariotaku.twidere.extension.model.notificationChannelId
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.notification.NotificationChannelSpec
import org.mariotaku.twidere.preference.TintedPreferenceCategory
@TargetApi(Build.VERSION_CODES.O)
class AccountNotificationChannelsPreference(context: Context, attrs: AttributeSet? = null) : TintedPreferenceCategory(context, attrs) {
var account: AccountDetails? = null
override fun onAttachedToHierarchy(preferenceManager: PreferenceManager?) {
super.onAttachedToHierarchy(preferenceManager)
initItems()
}
private fun initItems() {
removeAll()
val specs = NotificationChannelSpec.values().filter { it.grouped }.sortedBy { it.getName(context) }
specs.forEach { spec ->
val preference = Preference(context)
preference.title = spec.getName(context)
preference.summary = spec.getDescription(context)
preference.setOnPreferenceClickListener lambda@ {
val account = this.account ?: return@lambda true
val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
.putExtra(Settings.EXTRA_APP_PACKAGE, BuildConfig.APPLICATION_ID)
.putExtra(Settings.EXTRA_CHANNEL_ID, account.key.notificationChannelId(spec.id))
context.startActivity(intent)
return@lambda true
}
addPreference(preference)
}
}
}

View File

@ -21,6 +21,7 @@ package org.mariotaku.twidere.util
import android.content.Context
import android.content.SharedPreferences
import android.content.res.ColorStateList
import android.content.res.Resources
import android.graphics.Color
import android.graphics.PorterDuff
@ -458,6 +459,15 @@ object ThemeUtils {
}
}
fun getColorStateListFromAttribute(context: Context, @AttrRes attr: Int, styleRes: Int = 0): ColorStateList? {
val a = context.obtainStyledAttributes(null, intArrayOf(attr), 0, styleRes)
try {
return a.getColorStateList(0)
} finally {
a.recycle()
}
}
fun getBooleanFromAttribute(context: Context, @AttrRes attr: Int, styleRes: Int = 0, def: Boolean = false): Boolean {
val a = context.obtainStyledAttributes(null, intArrayOf(attr), 0, styleRes)
try {

View File

@ -31,6 +31,7 @@ import org.mariotaku.twidere.adapter.MessagesConversationAdapter
import org.mariotaku.twidere.extension.model.applyTo
import org.mariotaku.twidere.model.ParcelableMessage
import org.mariotaku.twidere.model.SpanItem
import org.mariotaku.twidere.util.ThemeUtils
import org.mariotaku.twidere.view.FixedTextView
import org.mariotaku.twidere.view.ProfileImageView
@ -64,8 +65,15 @@ class MessageViewHolder(itemView: View, adapter: MessagesConversationAdapter) :
override fun display(message: ParcelableMessage, showDate: Boolean) {
super.display(message, showDate)
messageBubble.bubbleColor = if (message.is_outgoing) {
adapter.bubbleColorOutgoing
} else {
adapter.bubbleColorIncoming
}
messageBubble.setOutgoing(message.is_outgoing)
text.setTextColor(ThemeUtils.getColorDependent(messageBubble.bubbleColor.defaultColor))
// Loop through text and spans to found non-space char count
val hideText = run {

View File

@ -55,6 +55,7 @@
android:layout_toEndOf="@+id/profileImage"
android:layout_toRightOf="@+id/profileImage"
android:clickable="true"
android:focusable="true"
android:minHeight="@dimen/profile_image_size_direct_message"
android:minWidth="@dimen/element_size_normal"
app:bubbleColor="?messageBubbleColor"

View File

@ -774,6 +774,8 @@
<string name="notification_channel_description_content_interactions">Interactions like mentions and retweets</string>
<string name="notification_channel_description_content_messages">Important messages like DMs</string>
<string name="notification_channel_descriptions_content_subscriptions">User\'s tweet notifications</string>
<string name="notification_channel_descriptions_content_updates">Updates like new tweets in home timeline</string>
<string name="notification_channel_name_app_notices">App notices</string>
<string name="notification_channel_name_background_progresses">Background operations</string>
<string name="notification_channel_name_content_interactions">Interactions</string>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--suppress AndroidElementNotAllowed -->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/notifications">
<org.mariotaku.twidere.preference.notification.AccountNotificationChannelsPreference
android:key="notification_channels"/>
<org.mariotaku.twidere.preference.TintedPreferenceCategory
android:key="cat_other_settings"
android:title="@string/other_settings">
<org.mariotaku.twidere.preference.ColorPickerPreference
android:key="notification_light_color"
android:title="@string/notification_light_color"
app:defaultColor="@color/branding_color"/>
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="notification_following_only"
android:summary="@string/following_only_summary"
android:title="@string/following_only"/>
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="notification_mentions_only"
android:title="@string/mentions_only"/>
</org.mariotaku.twidere.preference.TintedPreferenceCategory>
</PreferenceScreen>