parent
5a095a9178
commit
1d81c8cdf8
|
@ -38,7 +38,6 @@ import android.os.Build
|
|||
import android.os.Bundle
|
||||
import android.support.annotation.StringRes
|
||||
import android.support.v4.app.Fragment
|
||||
import android.support.v4.app.NotificationCompat
|
||||
import android.support.v4.view.GravityCompat
|
||||
import android.support.v4.view.ViewCompat
|
||||
import android.support.v4.view.ViewPager.OnPageChangeListener
|
||||
|
@ -79,6 +78,7 @@ import org.mariotaku.twidere.annotation.NavbarStyle
|
|||
import org.mariotaku.twidere.annotation.ReadPositionTag
|
||||
import org.mariotaku.twidere.constant.*
|
||||
import org.mariotaku.twidere.extension.applyTheme
|
||||
import org.mariotaku.twidere.extension.model.notificationBuilder
|
||||
import org.mariotaku.twidere.extension.onShow
|
||||
import org.mariotaku.twidere.fragment.AccountsDashboardFragment
|
||||
import org.mariotaku.twidere.fragment.BaseDialogFragment
|
||||
|
@ -91,6 +91,7 @@ import org.mariotaku.twidere.model.SupportTabSpec
|
|||
import org.mariotaku.twidere.model.Tab
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.event.UnreadCountUpdatedEvent
|
||||
import org.mariotaku.twidere.model.notification.NotificationChannelSpec
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Activities
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
|
||||
|
@ -99,6 +100,7 @@ import org.mariotaku.twidere.util.*
|
|||
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutCallback
|
||||
import org.mariotaku.twidere.view.HomeDrawerLayout
|
||||
import org.mariotaku.twidere.view.TabPagerIndicator
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, SupportFragmentCallback,
|
||||
OnLongClickListener, DrawerLayout.DrawerListener {
|
||||
|
@ -118,6 +120,18 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
|
|||
private val readStateChangeListener = OnSharedPreferenceChangeListener { _, _ -> updateUnreadCount() }
|
||||
private val controlBarShowHideHelper = ControlBarShowHideHelper(this)
|
||||
|
||||
override val controlBarHeight: Int
|
||||
get() {
|
||||
return mainTabs.height - mainTabs.stripHeight
|
||||
}
|
||||
|
||||
override val currentVisibleFragment: Fragment?
|
||||
get() {
|
||||
val currentItem = mainPager.currentItem
|
||||
if (currentItem < 0 || currentItem >= pagerAdapter.count) return null
|
||||
return pagerAdapter.instantiateItem(mainPager, currentItem)
|
||||
}
|
||||
|
||||
private val homeDrawerToggleDelegate = object : ActionBarDrawerToggle.Delegate {
|
||||
override fun setActionBarUpIndicator(upDrawable: Drawable, @StringRes contentDescRes: Int) {
|
||||
drawerToggleButton.setImageDrawable(upDrawable)
|
||||
|
@ -146,34 +160,17 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
|
|||
}
|
||||
}
|
||||
|
||||
override val controlBarHeight: Int
|
||||
get() {
|
||||
return mainTabs.height - mainTabs.stripHeight
|
||||
private val keyboardShortcutRecipient: Fragment?
|
||||
get() = when {
|
||||
homeMenu.isDrawerOpen(GravityCompat.START) -> leftDrawerFragment
|
||||
homeMenu.isDrawerOpen(GravityCompat.END) -> null
|
||||
else -> currentVisibleFragment
|
||||
}
|
||||
|
||||
fun closeAccountsDrawer() {
|
||||
if (homeMenu == null) return
|
||||
homeMenu.closeDrawers()
|
||||
}
|
||||
|
||||
private val activatedAccountKeys: Array<UserKey>
|
||||
get() = DataStoreUtils.getActivatedAccountKeys(this)
|
||||
|
||||
override val currentVisibleFragment: Fragment?
|
||||
get() {
|
||||
val currentItem = mainPager.currentItem
|
||||
if (currentItem < 0 || currentItem >= pagerAdapter.count) return null
|
||||
return pagerAdapter.instantiateItem(mainPager, currentItem)
|
||||
}
|
||||
|
||||
override fun triggerRefresh(position: Int): Boolean {
|
||||
val f = pagerAdapter.instantiateItem(mainPager, position)
|
||||
if (f.activity == null || f.isDetached) return false
|
||||
if (f !is RefreshScrollTopInterface) return false
|
||||
return f.triggerRefresh()
|
||||
}
|
||||
|
||||
val leftDrawerFragment: Fragment?
|
||||
private val leftDrawerFragment: Fragment?
|
||||
get() = supportFragmentManager.findFragmentById(R.id.leftDrawer)
|
||||
|
||||
private val isDrawerOpen: Boolean
|
||||
|
@ -220,7 +217,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
|
|||
ViewCompat.setOnApplyWindowInsetsListener(homeContent, this)
|
||||
homeMenu.fitsSystemWindows = Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP ||
|
||||
preferences[navbarStyleKey] != NavbarStyle.TRANSPARENT
|
||||
if (!homeMenu.fitsSystemWindows) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || !ViewCompat.getFitsSystemWindows(homeMenu)) {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(homeMenu, null)
|
||||
}
|
||||
|
||||
|
@ -544,6 +541,12 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
|
|||
return super.onKeyUp(keyCode, event)
|
||||
}
|
||||
|
||||
override fun triggerRefresh(position: Int): Boolean {
|
||||
val f = pagerAdapter.instantiateItem(mainPager, position)
|
||||
if (f.activity == null || f.isDetached) return false
|
||||
if (f !is RefreshScrollTopInterface) return false
|
||||
return f.triggerRefresh()
|
||||
}
|
||||
|
||||
fun notifyAccountsChanged() {
|
||||
}
|
||||
|
@ -553,11 +556,6 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
|
|||
updateUnreadCount()
|
||||
}
|
||||
|
||||
fun openSearchView(account: AccountDetails?) {
|
||||
selectedAccountToSearch = account
|
||||
onSearchRequested()
|
||||
}
|
||||
|
||||
fun updateUnreadCount() {
|
||||
if (mainTabs == null || updateUnreadCountTask != null && updateUnreadCountTask!!.status == AsyncTask.Status.RUNNING)
|
||||
return
|
||||
|
@ -580,10 +578,10 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
|
|||
if (mainTabs.columns > 1) {
|
||||
val lp = actionsButton.layoutParams
|
||||
val total: Float
|
||||
if (lp is MarginLayoutParams) {
|
||||
total = (lp.bottomMargin + actionsButton.height).toFloat()
|
||||
total = if (lp is MarginLayoutParams) {
|
||||
(lp.bottomMargin + actionsButton.height).toFloat()
|
||||
} else {
|
||||
total = actionsButton.height.toFloat()
|
||||
actionsButton.height.toFloat()
|
||||
}
|
||||
return 1 - actionsButton.translationY / total
|
||||
}
|
||||
|
@ -626,16 +624,15 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
|
|||
return homeDrawerToggleDelegate
|
||||
}
|
||||
|
||||
private val keyboardShortcutRecipient: Fragment?
|
||||
get() {
|
||||
if (homeMenu.isDrawerOpen(GravityCompat.START)) {
|
||||
return leftDrawerFragment
|
||||
} else if (homeMenu.isDrawerOpen(GravityCompat.END)) {
|
||||
return null
|
||||
} else {
|
||||
return currentVisibleFragment
|
||||
}
|
||||
}
|
||||
fun closeAccountsDrawer() {
|
||||
if (homeMenu == null) return
|
||||
homeMenu.closeDrawers()
|
||||
}
|
||||
|
||||
private fun openSearchView(account: AccountDetails?) {
|
||||
selectedAccountToSearch = account
|
||||
onSearchRequested()
|
||||
}
|
||||
|
||||
private fun handleFragmentKeyboardShortcutRepeat(handler: KeyboardShortcutsHandler,
|
||||
keyCode: Int, repeatCount: Int,
|
||||
|
@ -678,11 +675,10 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
|
|||
if (Intent.ACTION_SEARCH == action) {
|
||||
val query = intent.getStringExtra(SearchManager.QUERY)
|
||||
val appSearchData = intent.getBundleExtra(SearchManager.APP_DATA)
|
||||
val accountKey: UserKey?
|
||||
if (appSearchData != null && appSearchData.containsKey(EXTRA_ACCOUNT_KEY)) {
|
||||
accountKey = appSearchData.getParcelable<UserKey>(EXTRA_ACCOUNT_KEY)
|
||||
val accountKey = if (appSearchData != null && appSearchData.containsKey(EXTRA_ACCOUNT_KEY)) {
|
||||
appSearchData.getParcelable(EXTRA_ACCOUNT_KEY)
|
||||
} else {
|
||||
accountKey = Utils.getDefaultAccountKey(this)
|
||||
Utils.getDefaultAccountKey(this)
|
||||
}
|
||||
IntentUtils.openSearch(this, accountKey, query)
|
||||
return -1
|
||||
|
@ -847,7 +843,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
|
|||
}
|
||||
val intent = Intent(this, UsageStatisticsActivity::class.java)
|
||||
val contentIntent = PendingIntent.getActivity(this, 0, intent, 0)
|
||||
val builder = NotificationCompat.Builder(this)
|
||||
val builder = NotificationChannelSpec.appNotices.notificationBuilder(this)
|
||||
builder.setAutoCancel(true)
|
||||
builder.setSmallIcon(R.drawable.ic_stat_info)
|
||||
builder.setTicker(getString(R.string.usage_statistics))
|
||||
|
@ -889,7 +885,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
|
|||
}
|
||||
|
||||
|
||||
fun hasMultiColumns(): Boolean {
|
||||
private fun hasMultiColumns(): Boolean {
|
||||
if (!DeviceUtils.isDeviceTablet(this) || !DeviceUtils.isScreenTablet(this)) return false
|
||||
if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
return preferences.getBoolean("multi_column_tabs_landscape", resources.getBoolean(R.bool.default_multi_column_tabs_land))
|
||||
|
@ -907,17 +903,20 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
|
|||
}
|
||||
|
||||
private class UpdateUnreadCountTask(
|
||||
private val context: Context,
|
||||
context: Context,
|
||||
private val preferences: SharedPreferences,
|
||||
private val readStateManager: ReadStateManager,
|
||||
private val indicator: TabPagerIndicator,
|
||||
indicator: TabPagerIndicator,
|
||||
private val tabs: Array<SupportTabSpec>
|
||||
) : AsyncTask<Any, UpdateUnreadCountTask.TabBadge, SparseIntArray>() {
|
||||
|
||||
private val activatedKeys = DataStoreUtils.getActivatedAccountKeys(context)
|
||||
private val contextRef = WeakReference(context)
|
||||
private val indicatorRef = WeakReference(indicator)
|
||||
|
||||
override fun doInBackground(vararg params: Any): SparseIntArray {
|
||||
val result = SparseIntArray()
|
||||
val context = contextRef.get() ?: return result
|
||||
tabs.forEachIndexed { i, spec ->
|
||||
if (spec.type == null) {
|
||||
publishProgress(TabBadge(i, -1))
|
||||
|
@ -969,6 +968,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
|
|||
}
|
||||
|
||||
override fun onPostExecute(result: SparseIntArray) {
|
||||
val indicator = indicatorRef.get() ?: return
|
||||
indicator.clearBadge()
|
||||
for (i in 0 until result.size()) {
|
||||
indicator.setBadge(result.keyAt(i), result.valueAt(i))
|
||||
|
@ -976,6 +976,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
|
|||
}
|
||||
|
||||
override fun onProgressUpdate(vararg values: TabBadge) {
|
||||
val indicator = indicatorRef.get() ?: return
|
||||
for (value in values) {
|
||||
indicator.setBadge(value.index, value.count)
|
||||
}
|
||||
|
|
|
@ -35,9 +35,6 @@ import android.view.View
|
|||
import android.widget.Toast
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.Priority
|
||||
import com.bumptech.glide.load.resource.drawable.GlideDrawable
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import nl.komponents.kovenant.Promise
|
||||
import org.mariotaku.chameleon.Chameleon
|
||||
|
@ -52,7 +49,7 @@ import org.mariotaku.twidere.R
|
|||
import org.mariotaku.twidere.TwidereConstants.SHARED_PREFERENCES_NAME
|
||||
import org.mariotaku.twidere.activity.iface.IBaseActivity
|
||||
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_INTENT
|
||||
import org.mariotaku.twidere.constant.lastLaunchPresentationTimeKey
|
||||
import org.mariotaku.twidere.constant.lastLaunchTimeKey
|
||||
import org.mariotaku.twidere.constant.promotionsEnabledKey
|
||||
import org.mariotaku.twidere.constant.themeColorKey
|
||||
import org.mariotaku.twidere.constant.themeKey
|
||||
|
@ -148,14 +145,14 @@ open class MainActivity : ChameleonActivity(), IBaseActivity<MainActivity> {
|
|||
}
|
||||
|
||||
private fun showPresentationOrLaunch() {
|
||||
val lastLaunchPresentationTime = preferences[lastLaunchPresentationTimeKey]
|
||||
val lastLaunchTime = preferences[lastLaunchTimeKey]
|
||||
val maximumDuration = if (BuildConfig.DEBUG) {
|
||||
TimeUnit.SECONDS.toMillis(30)
|
||||
} else {
|
||||
TimeUnit.HOURS.toMillis(6)
|
||||
}
|
||||
// Show again at least 6 hours later (30 secs for debug builds)
|
||||
if (lastLaunchPresentationTime >= 0 && System.currentTimeMillis() - lastLaunchPresentationTime < maximumDuration) {
|
||||
if (lastLaunchTime >= 0 && System.currentTimeMillis() - lastLaunchTime < maximumDuration) {
|
||||
launchDirectly()
|
||||
return
|
||||
}
|
||||
|
@ -212,20 +209,6 @@ open class MainActivity : ChameleonActivity(), IBaseActivity<MainActivity> {
|
|||
skipPresentation.visibility = View.VISIBLE
|
||||
controlOverlay.tag = presentation
|
||||
Glide.with(this).load(presentation.images.first().url)
|
||||
.listener(object : RequestListener<String, GlideDrawable> {
|
||||
override fun onException(e: Exception?, model: String?,
|
||||
target: Target<GlideDrawable>?, isFirstResource: Boolean): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(resource: GlideDrawable?, model: String?,
|
||||
target: Target<GlideDrawable>?, isFromMemoryCache: Boolean,
|
||||
isFirstResource: Boolean): Boolean {
|
||||
preferences[lastLaunchPresentationTimeKey] = System.currentTimeMillis()
|
||||
return false
|
||||
}
|
||||
|
||||
})
|
||||
.priority(Priority.HIGH)
|
||||
.into(presentationView)
|
||||
}
|
||||
|
@ -245,6 +228,7 @@ open class MainActivity : ChameleonActivity(), IBaseActivity<MainActivity> {
|
|||
}
|
||||
|
||||
private fun performLaunch() {
|
||||
preferences[lastLaunchTimeKey] = System.currentTimeMillis()
|
||||
val am = AccountManager.get(this)
|
||||
if (!DeviceUtils.checkCompatibility()) {
|
||||
startActivity(Intent(this, IncompatibleAlertActivity::class.java))
|
||||
|
|
|
@ -65,6 +65,8 @@ import org.mariotaku.twidere.util.kovenant.stopKovenant
|
|||
import org.mariotaku.twidere.util.media.MediaPreloader
|
||||
import org.mariotaku.twidere.util.media.ThumborWrapper
|
||||
import org.mariotaku.twidere.util.net.TwidereDns
|
||||
import org.mariotaku.twidere.util.notification.ContentNotificationManager
|
||||
import org.mariotaku.twidere.util.notification.NotificationChannelsManager
|
||||
import org.mariotaku.twidere.util.premium.ExtraFeaturesService
|
||||
import org.mariotaku.twidere.util.refresh.AutoRefreshController
|
||||
import org.mariotaku.twidere.util.sync.DataSyncProvider
|
||||
|
@ -132,6 +134,7 @@ class TwidereApplication : Application(), Constants, OnSharedPreferenceChangeLis
|
|||
}
|
||||
super.onCreate()
|
||||
EmojioneTranslator.init(this)
|
||||
NotificationChannelsManager.createChannels(this)
|
||||
applyLanguageSettings()
|
||||
startKovenant()
|
||||
initializeAsyncTask()
|
||||
|
|
|
@ -85,7 +85,7 @@ val homeRefreshDirectMessagesKey = KBooleanKey(KEY_HOME_REFRESH_DIRECT_MESSAGES,
|
|||
val homeRefreshSavedSearchesKey = KBooleanKey(KEY_HOME_REFRESH_SAVED_SEARCHES, true)
|
||||
val composeStatusVisibilityKey = KNullableStringKey("compose_status_visibility", null)
|
||||
val navbarStyleKey = KStringKey(KEY_NAVBAR_STYLE, NavbarStyle.DEFAULT)
|
||||
val lastLaunchPresentationTimeKey = KLongKey("last_launch_presentation_time", -1)
|
||||
val lastLaunchTimeKey = KLongKey("last_launch_time", -1)
|
||||
val promotionsEnabledKey = KBooleanKey("promotions_enabled", false)
|
||||
|
||||
object cacheSizeLimitKey : KSimpleKey<Int>(KEY_CACHE_SIZE_LIMIT, 300) {
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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.extension.model
|
||||
|
||||
import android.content.Context
|
||||
import android.support.v4.app.NotificationCompat
|
||||
import org.mariotaku.twidere.model.notification.NotificationChannelSpec
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/8/25.
|
||||
*/
|
||||
fun NotificationChannelSpec.notificationBuilder(context: Context): NotificationCompat.Builder {
|
||||
return NotificationCompat.Builder(context, id)
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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.notification
|
||||
|
||||
import android.app.NotificationManager
|
||||
import android.support.annotation.StringRes
|
||||
import org.mariotaku.twidere.R
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/8/25.
|
||||
*/
|
||||
enum class NotificationChannelSpec(
|
||||
val id: String,
|
||||
@StringRes val nameRes: Int,
|
||||
@StringRes val descriptionRes: Int = 0,
|
||||
val importance: Int,
|
||||
val showBadge: Boolean = false) {
|
||||
/**
|
||||
* For notifications send by app itself.
|
||||
* Such as "what's new"
|
||||
*/
|
||||
appNotices("app_notices", R.string.notification_channel_name_app_notices,
|
||||
importance = NotificationManager.IMPORTANCE_LOW, showBadge = true),
|
||||
|
||||
/**
|
||||
* For notifications indicate that some lengthy operations are performing in the background.
|
||||
* Such as sending attachment process.
|
||||
*/
|
||||
backgroundProgresses("background_progresses", R.string.notification_channel_name_background_progresses,
|
||||
importance = NotificationManager.IMPORTANCE_MIN),
|
||||
|
||||
/**
|
||||
* For ongoing notifications indicating service statuses.
|
||||
* Such as notification showing streaming service running
|
||||
*/
|
||||
serviceStatuses("service_statuses", R.string.notification_channel_name_service_statuses,
|
||||
importance = NotificationManager.IMPORTANCE_MIN),
|
||||
|
||||
/**
|
||||
* For import notifications related to micro-blogging features.
|
||||
* Such as failure to update status.
|
||||
*/
|
||||
contentNotices("content_notices", R.string.notification_channel_name_content_notices,
|
||||
importance = NotificationManager.IMPORTANCE_HIGH, showBadge = true),
|
||||
/**
|
||||
* For updates related to micro-blogging features.
|
||||
* Such as new statuses posted by friends.
|
||||
*/
|
||||
contentUpdates("content_updates", R.string.notification_channel_name_content_updates,
|
||||
importance = NotificationManager.IMPORTANCE_DEFAULT, showBadge = 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,
|
||||
importance = NotificationManager.IMPORTANCE_HIGH, showBadge = true),
|
||||
/**
|
||||
* For interactions related to micro-blogging features.
|
||||
* Such as replies and likes.
|
||||
*/
|
||||
contentInteractions("content_interactions", R.string.notification_channel_name_content_interactions,
|
||||
descriptionRes = R.string.notification_channel_description_content_interactions,
|
||||
importance = NotificationManager.IMPORTANCE_HIGH, showBadge = true),
|
||||
/**
|
||||
* For messages related to micro-blogging features.
|
||||
* Such as direct messages.
|
||||
*/
|
||||
contentMessages("content_messages", R.string.notification_channel_name_content_messages,
|
||||
descriptionRes = R.string.notification_channel_description_content_messages,
|
||||
importance = NotificationManager.IMPORTANCE_HIGH, showBadge = true)
|
||||
|
||||
}
|
|
@ -54,6 +54,7 @@ import org.mariotaku.twidere.util.SQLiteDatabaseWrapper.LazyLoadCallback
|
|||
import org.mariotaku.twidere.util.dagger.GeneralComponent
|
||||
import org.mariotaku.twidere.util.database.CachedUsersQueryBuilder
|
||||
import org.mariotaku.twidere.util.database.SuggestionsCursorCreator
|
||||
import org.mariotaku.twidere.util.notification.ContentNotificationManager
|
||||
import java.util.concurrent.Executor
|
||||
import java.util.concurrent.Executors
|
||||
import javax.inject.Inject
|
||||
|
|
|
@ -26,6 +26,7 @@ import com.twitter.Extractor
|
|||
import com.twitter.Validator
|
||||
import org.mariotaku.twidere.util.*
|
||||
import org.mariotaku.twidere.util.dagger.GeneralComponent
|
||||
import org.mariotaku.twidere.util.notification.ContentNotificationManager
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class BaseService : Service() {
|
||||
|
|
|
@ -23,16 +23,13 @@ import android.accounts.AccountManager
|
|||
import android.annotation.SuppressLint
|
||||
import android.app.Notification
|
||||
import android.app.Service
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.provider.BaseColumns
|
||||
import android.support.annotation.UiThread
|
||||
import android.support.annotation.WorkerThread
|
||||
import android.support.v4.app.NotificationCompat
|
||||
import android.support.v4.app.NotificationCompat.Builder
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
|
@ -41,7 +38,6 @@ import nl.komponents.kovenant.ui.successUi
|
|||
import org.mariotaku.abstask.library.AbstractTask
|
||||
import org.mariotaku.abstask.library.ManualTaskStarter
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.ktextension.configure
|
||||
import org.mariotaku.ktextension.getNullableTypedArrayExtra
|
||||
import org.mariotaku.ktextension.toLongOr
|
||||
import org.mariotaku.ktextension.useCursor
|
||||
|
@ -58,9 +54,12 @@ import org.mariotaku.twidere.R
|
|||
import org.mariotaku.twidere.TwidereConstants.*
|
||||
import org.mariotaku.twidere.constant.refreshAfterTweetKey
|
||||
import org.mariotaku.twidere.extension.getErrorMessage
|
||||
import org.mariotaku.twidere.extension.model.notificationBuilder
|
||||
import org.mariotaku.twidere.extension.withAppendedPath
|
||||
import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtras
|
||||
import org.mariotaku.twidere.model.draft.StatusObjectActionExtras
|
||||
import org.mariotaku.twidere.model.notification.NotificationChannelSpec
|
||||
import org.mariotaku.twidere.model.schedule.ScheduleInfo
|
||||
import org.mariotaku.twidere.model.util.AccountUtils
|
||||
import org.mariotaku.twidere.model.util.ParcelableStatusUpdateUtils
|
||||
|
@ -183,7 +182,7 @@ class LengthyOperationsService : BaseIntentService("lengthy_operations") {
|
|||
|
||||
private fun sendMessage(message: ParcelableNewMessage) {
|
||||
val title = getString(R.string.sending_direct_message)
|
||||
val builder = Builder(this)
|
||||
val builder = NotificationChannelSpec.backgroundProgresses.notificationBuilder(this)
|
||||
builder.setSmallIcon(R.drawable.ic_stat_send)
|
||||
builder.setProgress(100, 0, true)
|
||||
builder.setTicker(title)
|
||||
|
@ -239,7 +238,7 @@ class LengthyOperationsService : BaseIntentService("lengthy_operations") {
|
|||
|
||||
private fun updateStatuses(statuses: Array<ParcelableStatusUpdate>, scheduleInfo: ScheduleInfo? = null) {
|
||||
val context = this
|
||||
val builder = Builder(context)
|
||||
val builder = NotificationChannelSpec.backgroundProgresses.notificationBuilder(context)
|
||||
startForeground(NOTIFICATION_ID_UPDATE_STATUS, updateUpdateStatusNotification(context,
|
||||
builder, 0, null))
|
||||
for (item in statuses) {
|
||||
|
@ -321,9 +320,7 @@ class LengthyOperationsService : BaseIntentService("lengthy_operations") {
|
|||
invokeAfterExecute(task, result)
|
||||
|
||||
if (!result.succeed) {
|
||||
contentResolver.insert(Drafts.CONTENT_URI_NOTIFICATIONS, configure(ContentValues()) {
|
||||
put(BaseColumns._ID, result.draftId)
|
||||
})
|
||||
contentResolver.insert(Drafts.CONTENT_URI_NOTIFICATIONS.withAppendedPath(result.draftId.toString()), null)
|
||||
}
|
||||
}
|
||||
if (preferences[refreshAfterTweetKey]) {
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.mariotaku.twidere.extension.model.api.key
|
|||
import org.mariotaku.twidere.extension.model.api.microblog.toParcelable
|
||||
import org.mariotaku.twidere.extension.model.api.toParcelable
|
||||
import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.notification.NotificationChannelSpec
|
||||
import org.mariotaku.twidere.model.pagination.SinceMaxPagination
|
||||
import org.mariotaku.twidere.model.util.AccountUtils
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.*
|
||||
|
@ -172,7 +173,7 @@ class StreamingService : BaseService() {
|
|||
val contentIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val contentTitle = getString(R.string.app_name)
|
||||
val contentText = getString(R.string.timeline_streaming_running)
|
||||
val builder = NotificationCompat.Builder(this)
|
||||
val builder = NotificationChannelSpec.serviceStatuses.notificationBuilder(this)
|
||||
builder.setOngoing(true)
|
||||
builder.setSmallIcon(R.drawable.ic_stat_streaming)
|
||||
builder.setContentTitle(contentTitle)
|
||||
|
|
|
@ -58,6 +58,7 @@ import org.mariotaku.twidere.util.media.MediaPreloader
|
|||
import org.mariotaku.twidere.util.media.ThumborWrapper
|
||||
import org.mariotaku.twidere.util.media.TwidereMediaDownloader
|
||||
import org.mariotaku.twidere.util.net.TwidereDns
|
||||
import org.mariotaku.twidere.util.notification.ContentNotificationManager
|
||||
import org.mariotaku.twidere.util.premium.ExtraFeaturesService
|
||||
import org.mariotaku.twidere.util.refresh.AutoRefreshController
|
||||
import org.mariotaku.twidere.util.refresh.JobSchedulerAutoRefreshController
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.util
|
||||
package org.mariotaku.twidere.util.notification
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.PendingIntent
|
||||
|
@ -48,21 +48,22 @@ import org.mariotaku.twidere.constant.nameFirstKey
|
|||
import org.mariotaku.twidere.extension.model.api.formattedTextWithIndices
|
||||
import org.mariotaku.twidere.extension.model.getSummaryText
|
||||
import org.mariotaku.twidere.extension.model.getTitle
|
||||
import org.mariotaku.twidere.extension.model.notificationBuilder
|
||||
import org.mariotaku.twidere.extension.model.notificationDisabled
|
||||
import org.mariotaku.twidere.extension.rawQuery
|
||||
import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.notification.NotificationChannelSpec
|
||||
import org.mariotaku.twidere.model.util.ParcelableActivityUtils
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.*
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations
|
||||
import org.mariotaku.twidere.receiver.NotificationReceiver
|
||||
import org.mariotaku.twidere.service.LengthyOperationsService
|
||||
import org.mariotaku.twidere.util.*
|
||||
import org.mariotaku.twidere.util.Utils
|
||||
import org.mariotaku.twidere.util.database.FilterQueryBuilder
|
||||
import org.oshkimaadziig.george.androidutils.SpanFormatter
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/16.
|
||||
*/
|
||||
class ContentNotificationManager(
|
||||
val context: Context,
|
||||
val activityTracker: ActivityTracker,
|
||||
|
@ -134,7 +135,7 @@ class ContentNotificationManager(
|
|||
}
|
||||
|
||||
// Setup notification
|
||||
val builder = NotificationCompat.Builder(context)
|
||||
val builder = NotificationChannelSpec.contentUpdates.notificationBuilder(context)
|
||||
builder.setAutoCancel(true)
|
||||
builder.setSmallIcon(R.drawable.ic_stat_twitter)
|
||||
builder.setTicker(notificationTitle)
|
||||
|
@ -175,7 +176,7 @@ class ContentNotificationManager(
|
|||
@SuppressLint("Recycle")
|
||||
val c = cr.query(Activities.AboutMe.CONTENT_URI, Activities.COLUMNS, where, whereArgs,
|
||||
OrderBy(Activities.TIMESTAMP, false).sql) ?: return
|
||||
val builder = NotificationCompat.Builder(context)
|
||||
val builder = NotificationChannelSpec.contentInteractions.notificationBuilder(context)
|
||||
val pebbleNotificationStringBuilder = StringBuilder()
|
||||
try {
|
||||
val count = c.count
|
||||
|
@ -277,18 +278,18 @@ class ContentNotificationManager(
|
|||
|
||||
var messageSum: Int = 0
|
||||
var newLastReadTimestamp = -1L
|
||||
cur.forEachRow { cur, _ ->
|
||||
val unreadCount = cur.getInt(indices[Conversations.UNREAD_COUNT])
|
||||
cur.forEachRow { c, _ ->
|
||||
val unreadCount = c.getInt(indices[Conversations.UNREAD_COUNT])
|
||||
if (unreadCount <= 0) return@forEachRow false
|
||||
if (newLastReadTimestamp != -1L) {
|
||||
newLastReadTimestamp = cur.getLong(indices[Conversations.LAST_READ_TIMESTAMP])
|
||||
newLastReadTimestamp = c.getLong(indices[Conversations.LAST_READ_TIMESTAMP])
|
||||
}
|
||||
messageSum += unreadCount
|
||||
return@forEachRow true
|
||||
}
|
||||
if (messageSum == 0) return
|
||||
|
||||
val builder = NotificationCompat.Builder(context)
|
||||
val builder = NotificationChannelSpec.contentMessages.notificationBuilder(context)
|
||||
applyNotificationPreferences(builder, pref, pref.directMessagesNotificationType)
|
||||
builder.setSmallIcon(R.drawable.ic_stat_message)
|
||||
builder.setCategory(NotificationCompat.CATEGORY_SOCIAL)
|
||||
|
@ -305,8 +306,8 @@ class ContentNotificationManager(
|
|||
builder.setDeleteIntent(getMarkReadDeleteIntent(context, NotificationType.DIRECT_MESSAGES,
|
||||
accountKey, newLastReadTimestamp, false))
|
||||
|
||||
val remaining = cur.forEachRow(5) { cur, pos ->
|
||||
val conversation = indices.newObject(cur)
|
||||
val remaining = cur.forEachRow(5) { c, pos ->
|
||||
val conversation = indices.newObject(c)
|
||||
if (conversation.notificationDisabled) return@forEachRow false
|
||||
val title = conversation.getTitle(context, userColorNameManager, nameFirst)
|
||||
val summary = conversation.getSummaryText(context, userColorNameManager, nameFirst)
|
||||
|
@ -336,7 +337,7 @@ class ContentNotificationManager(
|
|||
val userDisplayName = userColorNameManager.getDisplayName(status.user,
|
||||
preferences[nameFirstKey])
|
||||
val statusUri = LinkCreator.getTwidereStatusLink(accountKey, status.id)
|
||||
val builder = NotificationCompat.Builder(context)
|
||||
val builder = NotificationChannelSpec.contentSubscriptions.notificationBuilder(context)
|
||||
builder.color = userColorNameManager.getUserColor(userKey)
|
||||
builder.setAutoCancel(true)
|
||||
builder.setWhen(status.createdAt?.time ?: 0)
|
||||
|
@ -381,7 +382,7 @@ class ContentNotificationManager(
|
|||
uriBuilder.scheme(SCHEME_TWIDERE)
|
||||
uriBuilder.authority(AUTHORITY_DRAFTS)
|
||||
intent.data = uriBuilder.build()
|
||||
val nb = NotificationCompat.Builder(context)
|
||||
val nb = NotificationChannelSpec.contentNotices.notificationBuilder(context)
|
||||
nb.setTicker(message)
|
||||
nb.setContentTitle(title)
|
||||
nb.setContentText(item.text)
|
||||
|
@ -401,7 +402,6 @@ class ContentNotificationManager(
|
|||
PendingIntent.getService(context, 0, sendIntent, PendingIntent.FLAG_ONE_SHOT))
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
|
||||
nb.setContentIntent(PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT))
|
||||
nb.setGroup("drafts")
|
||||
notificationManager.notify(draftUri.toString(), NOTIFICATION_ID_DRAFTS, nb.build())
|
||||
return draftId
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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.notification
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationChannelGroup
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.twidere.constant.nameFirstKey
|
||||
import org.mariotaku.twidere.model.AccountDetails
|
||||
import org.mariotaku.twidere.model.notification.NotificationChannelSpec
|
||||
import org.mariotaku.twidere.util.dagger.DependencyHolder
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/8/25.
|
||||
*/
|
||||
object NotificationChannelsManager {
|
||||
fun createChannels(context: Context) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
||||
NotificationChannelCreatorImpl.createChannels(context)
|
||||
}
|
||||
|
||||
fun createAccountGroup(context: Context, account: AccountDetails) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
||||
NotificationChannelCreatorImpl.createAccountGroup(context, account)
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
private object NotificationChannelCreatorImpl {
|
||||
|
||||
fun createChannels(context: Context) {
|
||||
val nm = context.getSystemService(NotificationManager::class.java)
|
||||
val values = NotificationChannelSpec.values()
|
||||
nm.notificationChannels.filterNot { channel ->
|
||||
values.any { channel.id == it.id }
|
||||
}.forEach {
|
||||
nm.deleteNotificationChannel(it.id)
|
||||
}
|
||||
for (spec in values) {
|
||||
val channel = NotificationChannel(spec.id, context.getString(spec.nameRes), spec.importance)
|
||||
if (spec.descriptionRes != 0) {
|
||||
channel.description = context.getString(spec.descriptionRes)
|
||||
}
|
||||
channel.setShowBadge(spec.showBadge)
|
||||
nm.createNotificationChannel(channel)
|
||||
}
|
||||
}
|
||||
|
||||
fun createAccountGroup(context: Context, account: AccountDetails) {
|
||||
val nm = context.getSystemService(NotificationManager::class.java)
|
||||
val holder = DependencyHolder.get(context)
|
||||
val pref = holder.preferences
|
||||
val ucnm = holder.userColorNameManager
|
||||
val group = NotificationChannelGroup(account.key.toString(),
|
||||
ucnm.getDisplayName(account.user, pref[nameFirstKey]))
|
||||
nm.createNotificationChannelGroup(group)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -44,6 +44,7 @@
|
|||
<!-- [verb] Action for deleting a file or a twitter object like tweet-->
|
||||
<string name="action_delete">Delete</string>
|
||||
<string name="action_delete_messages">Delete messages</string>
|
||||
<string name="action_disable_promotions">Disable promotions</string>
|
||||
<string name="action_dont_restart">Don\'t restart</string>
|
||||
<string name="action_dont_terminate">Don\'t quit</string>
|
||||
<!-- [verb] Edit image/settings etc. -->
|
||||
|
@ -769,6 +770,16 @@
|
|||
|
||||
<string name="none">None</string>
|
||||
|
||||
<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_name_app_notices">App notices</string>
|
||||
<string name="notification_channel_name_background_progresses">Background operations</string>
|
||||
<string name="notification_channel_name_content_interactions">Content interactions</string>
|
||||
<string name="notification_channel_name_content_messages">Content messages</string>
|
||||
<string name="notification_channel_name_content_notices">Content notices</string>
|
||||
<string name="notification_channel_name_content_subscriptions">Content subscriptions</string>
|
||||
<string name="notification_channel_name_content_updates">Content updates</string>
|
||||
<string name="notification_channel_name_service_statuses">Service statuses</string>
|
||||
<string name="notification_direct_message"><xliff:g id="user">%s</xliff:g> sent you a direct message.</string>
|
||||
<string name="notification_direct_message_multiple_messages"><xliff:g id="user">%1$s</xliff:g> sent you <xliff:g id="messages_count">%2$d</xliff:g> direct messages.</string>
|
||||
<string name="notification_direct_message_multiple_users"><xliff:g id="user">%1$s</xliff:g> and <xliff:g id="users_count">%2$d</xliff:g> others sent you <xliff:g id="messages_count">%3$d</xliff:g> direct messages.</string>
|
||||
|
@ -1329,5 +1340,4 @@
|
|||
<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="action_disable_promotions">Disable promotions</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue