Skip to content

Commit

Permalink
Add option to allow check orientation before setting wallpaper (#19)
Browse files Browse the repository at this point in the history
Some modified Android that can't set wallpaper correctly if the device isn't
in natural orientation, start a foreground service to detect rotation changes
when this option is enabled.
  • Loading branch information
0ranko0P committed Jan 28, 2022
1 parent 30d2c6d commit dfdd352
Show file tree
Hide file tree
Showing 11 changed files with 265 additions and 32 deletions.
5 changes: 5 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@
android:enabled="true"
android:exported="false" />

<service
android:name=".services.RotationListenerService"
android:enabled="true"
android:exported="false" />

<meta-data
android:name="xposedmodule"
android:value="true" />
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/java/me/ranko/autodark/Utils/ViewUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,10 @@ object ViewUtil {
ta.recycle()
return colorAccent
}

@Suppress("DEPRECATION")
fun getRotation(context: Context): Int {
val window = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
return window.defaultDisplay.rotation
}
}
16 changes: 16 additions & 0 deletions app/src/main/java/me/ranko/autodark/core/WallpaperSetterBinder.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package me.ranko.autodark.core

import android.os.Binder

abstract class WallpaperSetterBinder : Binder() {

interface WallpaperSetterServiceCallback {
fun onReadyToSet()

fun onServiceFailure(e: Exception)
}

abstract fun start(callback: WallpaperSetterServiceCallback)

abstract fun destroy()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package me.ranko.autodark.core

import android.content.ComponentName
import android.content.Context
import android.content.ServiceConnection
import android.os.IBinder
import com.android.wallpaper.asset.StreamableAsset
import com.android.wallpaper.model.LiveWallpaperInfo
import com.android.wallpaper.model.WallpaperInfo
import com.android.wallpaper.module.WallpaperPersister
import com.android.wallpaper.module.WallpaperSetter

class WallpaperSetterConnection(
private val context: Context,
private val wallpapers: Pair<WallpaperInfo, WallpaperInfo?>,
private val callback: WallpaperPersister.SetWallpaperCallback,
private val setter: WallpaperSetter
) : ServiceConnection, WallpaperSetterBinder.WallpaperSetterServiceCallback,
WallpaperPersister.SetWallpaperCallback {

private var binder: WallpaperSetterBinder? = null

constructor(
context: Context,
wallpaper: LiveWallpaperInfo,
callback: WallpaperPersister.SetWallpaperCallback,
setter: WallpaperSetter
) : this(context, Pair(wallpaper, null), callback, setter)

override fun onServiceConnected(name: ComponentName, service: IBinder) {
binder = (service as WallpaperSetterBinder)
binder!!.start(this)
}

override fun onServiceDisconnected(name: ComponentName) {
// no-op
}

override fun onReadyToSet() {
val home = wallpapers.first
if (home is LiveWallpaperInfo) {
setter.setCurrentLiveWallpaper(home, this)
} else {
val lockAsset: StreamableAsset? = wallpapers.second?.getAsset(context)?.let {
it as StreamableAsset
}
setter.setDarkWallpapers(home.getAsset(context) as StreamableAsset, lockAsset, this)
}
}

override fun onServiceFailure(e: Exception) {
this.onError(e)
}

override fun onSuccess(id: String) {
callback.onSuccess(id)
destroy()
}

override fun onError(e: java.lang.Exception?) {
super.onError(e)
callback.onError(e)
destroy()
}

private fun destroy() {
context.unbindService(this)
binder!!.destroy()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package me.ranko.autodark.services

import android.app.*
import android.content.Context
import android.content.Intent
import android.os.IBinder
import android.view.OrientationEventListener
import android.view.Surface
import android.view.WindowManager
import kotlinx.coroutines.*
import me.ranko.autodark.R
import me.ranko.autodark.core.WallpaperSetterBinder
import me.ranko.autodark.core.WallpaperSetterBinder.WallpaperSetterServiceCallback
import me.ranko.autodark.core.WallpaperSetterConnection

/**
* Service that listens for orientation changes.
*
* Some modified Android skins cropped wallpaper based on current screen orientation,
* applying wallpaper when the device is in the right rotation [Surface.ROTATION_0].
* */
class RotationListenerService: Service() {

private val mBinder = ListenerBinder()

private var sensorListener: OrientationListener? = null

private class OrientationListener(
context: Application,
var callback: WallpaperSetterServiceCallback?
) : OrientationEventListener(context) {

@Suppress("DEPRECATION")
private val mDisplay =
(context.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay

override fun onOrientationChanged(orientation: Int) {
if (mDisplay.rotation == Surface.ROTATION_0) {
callback!!.onReadyToSet()
disable()
}
}

override fun enable() {
if (canDetectOrientation()) {
super.enable()
} else {
// Address it as cancelled, so helper won't disable next wallpaper alarm
callback!!.onServiceFailure(CancellationException("Orientation sensor not available, abort."))
disable()
}
}

override fun disable(){
super.disable()
callback = null
}
}

private inner class ListenerBinder : WallpaperSetterBinder() {
private var callback: WallpaperSetterServiceCallback? = null

override fun start(callback: WallpaperSetterServiceCallback) {
this.callback = callback
sensorListener = OrientationListener(application, callback)
sensorListener!!.enable()
}

override fun destroy() {
sensorListener = null
stopForeground(true)
stopSelf()
}
}

override fun onCreate() {
val mManager = getSystemService(NotificationManager::class.java)
val channel = NotificationChannel(
ROTATION_SERVICE_CHANNEL,
getString(R.string.service_rotation_name),
NotificationManager.IMPORTANCE_LOW
)
mManager.createNotificationChannel(channel)

val builder = Notification.Builder(this, ROTATION_SERVICE_CHANNEL)
builder.setSmallIcon(R.drawable.ic_auto_dark)
builder.setContentTitle(channel.name)
builder.setContentText(getString(R.string.service_rotation_listening))
startForeground(ROTATION_SERVICE_ID, builder.build())
}

override fun onBind(intent: Intent?): IBinder = mBinder

companion object {
private const val ROTATION_SERVICE_CHANNEL = "ROTATION"
private const val ROTATION_SERVICE_ID = 12

fun startForegroundService(context: Context, connection: WallpaperSetterConnection) {
val intent = Intent(context, RotationListenerService::class.java)
context.bindService(intent, connection, BIND_AUTO_CREATE)
context.startForegroundService(intent)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ class DarkWallpaperFragment : PreviewFragment(), ViewTreeObserver.OnGlobalLayout
viewModel.deleteAvailable.observe(this, Observer { available ->
mMenuDelete?.isVisible = available
})
menu.findItem(R.id.action_orientation)?.isChecked = viewModel.shouldCheckOrientation()
}

override fun onOptionsItemSelected(item: MenuItem): Boolean {
Expand All @@ -373,6 +374,11 @@ class DarkWallpaperFragment : PreviewFragment(), ViewTreeObserver.OnGlobalLayout

R.id.action_delete -> showDeleteConfirmDialog()

R.id.action_orientation -> {
viewModel.setCheckOrientation(item.isChecked.not())
item.isChecked = item.isChecked.not()
}

else -> return super.onOptionsItemSelected(item)
}
return true
Expand Down
66 changes: 36 additions & 30 deletions app/src/main/java/me/ranko/autodark/ui/DarkWallpaperHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ import android.content.ComponentName
import android.content.Context
import android.content.Context.BIND_AUTO_CREATE
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import android.util.ArrayMap
import android.view.Surface
import android.widget.Toast
import androidx.annotation.VisibleForTesting
import com.android.wallpaper.asset.BuiltInWallpaperAsset
Expand All @@ -21,12 +20,12 @@ import com.android.wallpaper.module.WallpaperPersister.*
import com.android.wallpaper.module.WallpaperSetter
import kotlinx.coroutines.*
import me.ranko.autodark.R
import me.ranko.autodark.core.DarkModeSettings
import me.ranko.autodark.core.ShizukuApi
import me.ranko.autodark.core.ShizukuStatus
import me.ranko.autodark.Utils.ViewUtil
import me.ranko.autodark.core.*
import me.ranko.autodark.model.*
import me.ranko.autodark.model.PersistableWallpaper.Companion.getWallpaperFile
import me.ranko.autodark.services.DarkLiveWallpaperService
import me.ranko.autodark.services.RotationListenerService
import me.ranko.autodark.ui.WallpaperType.DARK_HOME
import me.ranko.autodark.ui.WallpaperType.HOME
import timber.log.Timber
Expand Down Expand Up @@ -54,6 +53,8 @@ class DarkWallpaperHelper private constructor(private val mContext: Context) {
private const val KEY_HIDE_SHIZUKU_WARNING = "hideShizuku"
private const val KEY_LAST_SETTING_SUCCEED = "NoErr"

private const val KEY_CHECK_ROTATION = "check_orientation"

@SuppressLint("StaticFieldLeak")
@Volatile
private var INSTANCE: DarkWallpaperHelper? = null
Expand All @@ -77,17 +78,6 @@ class DarkWallpaperHelper private constructor(private val mContext: Context) {
}
return false
}

private class DarkWallpaperConnection(val callback: SetWallpaperCallback): ServiceConnection {

override fun onServiceConnected(name: ComponentName, service: IBinder) {
(service as DarkLiveWallpaperService.DarkBinder).start(callback)
}

override fun onServiceDisconnected(name: ComponentName) {
// no-op
}
}
}

inner class DefaultWallpaperSetterCallback : SetWallpaperCallback {
Expand All @@ -111,10 +101,7 @@ class DarkWallpaperHelper private constructor(private val mContext: Context) {
}

private fun destroy() {
if (isApplyingLiveWallpaper()) {
mContext.unbindService(connection!!)
connection = null
}
if (isApplyingLiveWallpaper()) connection = null

if (viewModelCallback == null) this@DarkWallpaperHelper.destroy()
}
Expand All @@ -139,7 +126,7 @@ class DarkWallpaperHelper private constructor(private val mContext: Context) {

private var mLiveWallpapers: ArrayMap<ComponentName, LiveWallpaperInfo>? = null

private var connection: DarkWallpaperConnection? = null
private var connection: WallpaperSetterConnection? = null

/**
* Notify wallpaper apply result to ViewModel.
Expand Down Expand Up @@ -227,11 +214,11 @@ class DarkWallpaperHelper private constructor(private val mContext: Context) {
@VisibleForTesting
suspend fun applyWallpaper(darkMode: Boolean) {
val wallpapers: List<WallpaperInfo>? = mPersisted?.asList() ?: readJson()

if (wallpapers == null) {
Timber.e("Error while getting persisted wallpapers, abort.")
return
}

val index = if (darkMode) DARK_HOME.ordinal else HOME.ordinal
val callback = DefaultWallpaperSetterCallback()
val home = wallpapers[index]
Expand All @@ -240,25 +227,31 @@ class DarkWallpaperHelper private constructor(private val mContext: Context) {
Timber.d("Setting LiveWallpaper id: %s.", home.wallpaperId)
applyLiveWallpaper(home, callback)
} else {
val lock = wallpapers[index + 1] as DarkWallpaperInfo
Timber.d("Applying Wallpaper, homeId: %s, lockId: %s.", home.wallpaperId, lock.wallpaperId)
val homeAsset = home.getAsset(mContext) as StreamableAsset
if (home == lock) {
mSetter.setDarkWallpapers(homeAsset, null, callback)
val lock: DarkWallpaperInfo? = (wallpapers[index + 1]).let {
if (it.wallpaperId == home.wallpaperId) null else it as DarkWallpaperInfo
}

Timber.d("Applying Wallpaper, home:%s, lock:%s.", home.wallpaperId, lock?.wallpaperId)
val checkRotation = shouldCheckOrientation()
if (checkRotation && ViewUtil.getRotation(mContext) != Surface.ROTATION_0) {
Timber.d("Illegal orientation, starting listener service")
connection = WallpaperSetterConnection(mContext, Pair(home, lock), callback, mSetter)
RotationListenerService.startForegroundService(mContext, connection!!)
} else {
mSetter.setDarkWallpapers(homeAsset, lock.getAsset(mContext) as StreamableAsset, callback)
val homeAsset = home.getAsset(mContext) as StreamableAsset
val lockAsset = lock?.let { it.getAsset(mContext) as StreamableAsset }
mSetter.setDarkWallpapers(homeAsset, lockAsset, callback)
}
}
}

@VisibleForTesting
fun applyLiveWallpaper(wallpaper: LiveWallpaperInfo, callback: SetWallpaperCallback) {
when (ShizukuApi.checkShizukuCompat(mContext)) {

ShizukuStatus.AVAILABLE -> mSetter.setCurrentLiveWallpaper(wallpaper, callback)

ShizukuStatus.DEAD -> {
connection = DarkWallpaperConnection(callback)
connection = WallpaperSetterConnection(mContext, wallpaper, callback, mSetter)
with(mContext) {
val intent = Intent(this, DarkLiveWallpaperService::class.java)
intent.putExtra(DarkLiveWallpaperService.ARG_TARGET_WALLPAPER, wallpaper)
Expand Down Expand Up @@ -647,6 +640,19 @@ class DarkWallpaperHelper private constructor(private val mContext: Context) {

fun isShizukuDismissed(): Boolean = mPreference.getBoolean(KEY_HIDE_SHIZUKU_WARNING, false)

/**
* Check screen orientation before setting wallpaper in the background.
* A workaround for some modified Android that can't set wallpaper correctly.
*
* @see applyWallpaper
* @see RotationListenerService
* */
fun shouldCheckOrientation(): Boolean = mPreference.getBoolean(KEY_CHECK_ROTATION, false)

fun setCheckOrientation(check: Boolean) {
mPreference.edit().putBoolean(KEY_CHECK_ROTATION, check).apply()
}

fun dismissShizuku() {
mPreference.edit().putBoolean(KEY_HIDE_SHIZUKU_WARNING, true).apply()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,10 @@ class DarkWallpaperPickerViewModel(application: Application) : ShizukuViewModel(
return e
}

fun shouldCheckOrientation(): Boolean = mHelper.shouldCheckOrientation()

fun setCheckOrientation(check: Boolean) = mHelper.setCheckOrientation(check)

companion object {
class Factory(private val application: Application) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
Expand Down
Loading

0 comments on commit dfdd352

Please sign in to comment.