mirror of
https://github.com/TwidereProject/Twidere-Android
synced 2025-02-02 17:56:56 +01:00
distinguished incoming and outgoing message with color
supported notification channel settings on Android 8.0
This commit is contained in:
parent
b4224b099d
commit
20d014e370
168
gradlew.bat
vendored
168
gradlew.bat
vendored
@ -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
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
|
@ -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>
|
Loading…
x
Reference in New Issue
Block a user