Skip to content

Commit

Permalink
Merge pull request #984 from divadsn/snow
Browse files Browse the repository at this point in the history
#MoarSnow
  • Loading branch information
divadsn authored Dec 6, 2017
2 parents 029a8be + 8ddafcf commit 14f0437
Show file tree
Hide file tree
Showing 20 changed files with 405 additions and 1 deletion.
7 changes: 7 additions & 0 deletions app/src/main/java/ch/deletescape/lawnchair/Launcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ public void onReceive(Context context, Intent intent) {

private boolean mPaused = true;
private boolean mOnResumeNeedsLoad;
private boolean mSnowfallEnabled;
private boolean mPlanesEnabled;
private ObjectAnimator mPlanesAnimator;

Expand Down Expand Up @@ -400,6 +401,7 @@ protected void onCreate(Bundle savedInstanceState) {

setContentView(R.layout.launcher);

mSnowfallEnabled = Utilities.getPrefs(this).getEnableSnowfall();
mPlanesEnabled = Utilities.getPrefs(this).getEnablePlanes();
setupViews();
mDeviceProfile.layout(this, false /* notifyListeners */);
Expand Down Expand Up @@ -1126,6 +1128,11 @@ private void setupViews() {
mQsbContainer = mDragLayer.findViewById(R.id.qsb_container);
mWorkspace.initParentViews(mDragLayer);

if (mSnowfallEnabled) {
Log.d(TAG, "inflating snowfall");
getLayoutInflater().inflate(mPlanesEnabled ? R.layout.snowfall_planes : R.layout.snowfall, (ViewGroup) findViewById(R.id.launcher_background), true);
}

if (mPlanesEnabled) {
Log.d(TAG, "inflating planes");
getLayoutInflater().inflate(R.layout.planes, (ViewGroup) mLauncherView, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ object FeatureFlags {
const val KEY_PREF_PULLDOWN_ACTION = "pref_pulldownAction"
const val KEY_PREF_LOCK_DESKTOP = "pref_lockDesktop"
const val KEY_PREF_ANIMATED_CLOCK_ICON = "pref_animatedClockIcon"
const val KEY_PREF_SNOWFALL = "pref_snowfall"
private var darkThemeFlag: Int = 0

const val DARK_QSB = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ interface IPreferenceProvider {
val iconLabelsInTwoLines: Boolean
val twoRowDock: Boolean
val pulldownAction: String
val enableSnowfall: Boolean

// -----------------
// PREFERENCES
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ object PreferenceFlags {
const val KEY_PREF_DRAWER_CUSTOM_LABEL_COLOR_HUE = "pref_allAppsCustomLabelColorHue"
const val KEY_PREF_DRAWER_CUSTOM_LABEL_COLOR_VARITATION = "pref_allAppsCustomLabelColorVariation"
const val KEY_PREF_DRAWER_VERTICAL_LAYOUT = "pref_verticalDrawerLayout"
const val KEY_PREF_SNOWFALL = "pref_snowfall"

const val KEY_APP_VISIBILITY_PREFIX = "visibility_"
const val KEY_PREVIOUS_BUILD_NUMBER = "previousBuildNumber"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ open class PreferenceImpl(context: Context) : IPreferenceProvider {
override val showWeather by BooleanPref(FeatureFlags.KEY_PREF_WEATHER, false)
override val lockDesktop by BooleanPref(FeatureFlags.KEY_PREF_LOCK_DESKTOP, false)
override val animatedClockIcon by BooleanPref(FeatureFlags.KEY_PREF_ANIMATED_CLOCK_ICON, false)
override val enableSnowfall by BooleanPref(FeatureFlags.KEY_PREF_SNOWFALL, false)

override val pinchToOverview by BooleanPref(FeatureFlags.KEY_PREF_PINCH_TO_OVERVIEW, true)
override val centerWallpaper by BooleanPref(PreferenceFlags.KEY_CENTER_WALLPAPER, true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ public void run() {
case PreferenceFlags.KEY_PREF_HOTSEAT_SHOW_ARROW:
case PreferenceFlags.KEY_PREF_HOTSEAT_SHOW_PAGE_INDICATOR:
case PreferenceFlags.KEY_TWO_ROW_DOCK:
case PreferenceFlags.KEY_PREF_SNOWFALL:
mLauncher.scheduleKill();
case PreferenceFlags.KEY_BACKPORT_ADAPTIVE_ICONS:
mLauncher.scheduleReloadIcons();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,18 @@ public boolean onItemLongClick(AdapterView<?> parent, View view, int position, l
/**
* This fragment shows the launcher preferences.
*/
public static class LauncherSettingsFragment extends BaseFragment {
public static class LauncherSettingsFragment extends BaseFragment implements Preference.OnPreferenceChangeListener {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);

Preference prefSnowfallEnabled = findPreference(FeatureFlags.KEY_PREF_SNOWFALL);
prefSnowfallEnabled.setOnPreferenceChangeListener(this);
if (Utilities.getPrefs(getActivity()).getEnableSnowfall()) {
prefSnowfallEnabled.setSummary(R.string.snowfall_enabled);
}
}

@Override
Expand All @@ -190,6 +196,19 @@ public void onResume() {
super.onResume();
getActivity().setTitle(R.string.settings_button_text);
}

@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference.getKey() != null) {
switch (preference.getKey()) {
case FeatureFlags.KEY_PREF_SNOWFALL:
findPreference(FeatureFlags.KEY_PREF_SNOWFALL).setSummary((boolean) newValue ? R.string.snowfall_enabled : R.string.snowfall_summary);
break;
}
return true;
}
return false;
}
}

public static class SubSettingsFragment extends BaseFragment implements Preference.OnPreferenceChangeListener {
Expand Down
38 changes: 38 additions & 0 deletions app/src/main/java/ch/deletescape/lawnchair/snow/Drawables.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package ch.deletescape.lawnchair.snow

import android.annotation.SuppressLint
import android.annotation.TargetApi
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.VectorDrawable
import android.os.Build

import ch.deletescape.lawnchair.util.Randomizer

/**
* Copyright (C) 2016 JetRadar, licensed under Apache License 2.0
* https://github.com/JetradarMobile/android-snowfall/
*/

@SuppressLint("NewApi")
internal fun Drawable.toBitmap(rotation: Float): Bitmap {
return when (this) {
is BitmapDrawable -> bitmap
is VectorDrawable -> toBitmap(rotation)
else -> throw IllegalArgumentException("Unsupported drawable type")
}
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
internal fun VectorDrawable.toBitmap(rotation: Float): Bitmap {
val bitmap = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
canvas.rotate(rotation, intrinsicWidth / 2f, intrinsicHeight / 2f);
setBounds(0, 0, canvas.width, canvas.height)
setTint(Color.WHITE)
draw(canvas)
return bitmap
}
127 changes: 127 additions & 0 deletions app/src/main/java/ch/deletescape/lawnchair/snow/SnowfallView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package ch.deletescape.lawnchair.snow

import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.os.Handler
import android.os.HandlerThread
import android.util.AttributeSet
import android.view.View

import ch.deletescape.lawnchair.R
import java.util.*

/**
* Copyright (C) 2016 JetRadar, licensed under Apache License 2.0
* https://github.com/JetradarMobile/android-snowfall/
*/
class SnowfallView(context: Context, attrs: AttributeSet) : View(context, attrs) {
private val DEFAULT_SNOWFLAKES_NUM = 200
private val DEFAULT_SNOWFLAKE_ALPHA_MIN = 150
private val DEFAULT_SNOWFLAKE_ALPHA_MAX = 250
private val DEFAULT_SNOWFLAKE_ANGLE_MAX = 10
private val DEFAULT_SNOWFLAKE_SIZE_MIN_IN_DP = 2
private val DEFAULT_SNOWFLAKE_SIZE_MAX_IN_DP = 8
private val DEFAULT_SNOWFLAKE_SPEED_MIN = 2
private val DEFAULT_SNOWFLAKE_SPEED_MAX = 8
private val DEFAULT_SNOWFLAKES_FADING_ENABLED = false
private val DEFAULT_SNOWFLAKES_ALREADY_FALLING = false

private val snowflakesNum: Int
private val snowflakeImage: Bitmap?
private val snowflakeAlphaMin: Int
private val snowflakeAlphaMax: Int
private val snowflakeAngleMax: Int
private val snowflakeSizeMinInPx: Int
private val snowflakeSizeMaxInPx: Int
private val snowflakeSpeedMin: Int
private val snowflakeSpeedMax: Int
private val snowflakesFadingEnabled: Boolean
private val snowflakesAlreadyFalling: Boolean

private val updateSnowflakesThread: UpdateSnowflakesThread
private var snowflakes: Array<Snowflake>? = null

private var rotationAngles = floatArrayOf(45f, 135f, 225f, 315f)

init {
val attrs = context.obtainStyledAttributes(attrs, R.styleable.SnowfallView)
val rotation = rotationAngles[Random().nextInt(4)]

try {
snowflakesNum = attrs.getInt(R.styleable.SnowfallView_snowflakesNum, DEFAULT_SNOWFLAKES_NUM)
snowflakeImage = attrs.getDrawable(R.styleable.SnowfallView_snowflakeImage)?.toBitmap(rotation)
snowflakeAlphaMin = attrs.getInt(R.styleable.SnowfallView_snowflakeAlphaMin, DEFAULT_SNOWFLAKE_ALPHA_MIN)
snowflakeAlphaMax = attrs.getInt(R.styleable.SnowfallView_snowflakeAlphaMax, DEFAULT_SNOWFLAKE_ALPHA_MAX)
snowflakeAngleMax = attrs.getInt(R.styleable.SnowfallView_snowflakeAngleMax, DEFAULT_SNOWFLAKE_ANGLE_MAX)
snowflakeSizeMinInPx = attrs.getDimensionPixelSize(R.styleable.SnowfallView_snowflakeSizeMin, dpToPx(DEFAULT_SNOWFLAKE_SIZE_MIN_IN_DP))
snowflakeSizeMaxInPx = attrs.getDimensionPixelSize(R.styleable.SnowfallView_snowflakeSizeMax, dpToPx(DEFAULT_SNOWFLAKE_SIZE_MAX_IN_DP))
snowflakeSpeedMin = attrs.getInt(R.styleable.SnowfallView_snowflakeSpeedMin, DEFAULT_SNOWFLAKE_SPEED_MIN)
snowflakeSpeedMax = attrs.getInt(R.styleable.SnowfallView_snowflakeSpeedMax, DEFAULT_SNOWFLAKE_SPEED_MAX)
snowflakesFadingEnabled = attrs.getBoolean(R.styleable.SnowfallView_snowflakesFadingEnabled, DEFAULT_SNOWFLAKES_FADING_ENABLED)
snowflakesAlreadyFalling = attrs.getBoolean(R.styleable.SnowfallView_snowflakesAlreadyFalling, DEFAULT_SNOWFLAKES_ALREADY_FALLING)
} finally {
attrs.recycle()
}

updateSnowflakesThread = UpdateSnowflakesThread()
}

private fun dpToPx(dp: Int): Int {
return (dp * resources.displayMetrics.density).toInt()
}

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
snowflakes = createSnowflakes()
}

override fun onVisibilityChanged(changedView: View, visibility: Int) {
super.onVisibilityChanged(changedView, visibility)
if (changedView === this && visibility == GONE) {
snowflakes?.forEach { it.reset() }
}
}

override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (isInEditMode) {
return
}

snowflakes?.forEach { it.draw(canvas) }
updateSnowflakes()
}

private fun createSnowflakes(): Array<Snowflake> {
val snowflakeParams = Snowflake.Params(
parentWidth = width,
parentHeight = height,
image = snowflakeImage,
alphaMin = snowflakeAlphaMin,
alphaMax = snowflakeAlphaMax,
angleMax = snowflakeAngleMax,
sizeMinInPx = snowflakeSizeMinInPx,
sizeMaxInPx = snowflakeSizeMaxInPx,
speedMin = snowflakeSpeedMin,
speedMax = snowflakeSpeedMax,
fadingEnabled = snowflakesFadingEnabled,
alreadyFalling = snowflakesAlreadyFalling)
return Array(snowflakesNum, { Snowflake(snowflakeParams) })
}

private fun updateSnowflakes() {
updateSnowflakesThread.handler.post {
snowflakes?.forEach { it.update() }
postInvalidateOnAnimation()
}
}

private inner class UpdateSnowflakesThread : HandlerThread("SnowflakesComputations") {
val handler by lazy { Handler(looper) }

init {
start()
}
}
}
100 changes: 100 additions & 0 deletions app/src/main/java/ch/deletescape/lawnchair/snow/Snowflake.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package ch.deletescape.lawnchair.snow

import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Paint.Style

import ch.deletescape.lawnchair.util.Randomizer

import java.lang.Math.cos
import java.lang.Math.sin
import java.lang.Math.toRadians

/**
* Copyright (C) 2016 JetRadar, licensed under Apache License 2.0
* https://github.com/JetradarMobile/android-snowfall/
*/
internal class Snowflake(val params: Params) {
private var size: Int = 0
private var alpha: Int = 255
private var bitmap: Bitmap? = null
private var speedX: Double = 0.0
private var speedY: Double = 0.0
private var positionX: Double = 0.0
private var positionY: Double = 0.0

private val paint by lazy {
Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.rgb(255, 255, 255)
style = Style.FILL
}
}

private val randomizer by lazy { Randomizer() }

init {
reset()
}

internal fun reset(positionY: Double? = null) {
size = randomizer.randomInt(params.sizeMinInPx, params.sizeMaxInPx, gaussian = true)
if (params.image != null) {
bitmap = Bitmap.createScaledBitmap(params.image, size, size, false)
}

val speed = ((size - params.sizeMinInPx).toFloat() / (params.sizeMaxInPx - params.sizeMinInPx) * (params.speedMax - params.speedMin) + params.speedMin)
val angle = toRadians(randomizer.randomDouble(params.angleMax) * randomizer.randomSignum())
speedX = speed * sin(angle)
speedY = speed * cos(angle)

alpha = randomizer.randomInt(params.alphaMin, params.alphaMax)
paint.alpha = alpha

positionX = randomizer.randomDouble(params.parentWidth)
if (positionY != null) {
this.positionY = positionY
} else {
this.positionY = randomizer.randomDouble(params.parentHeight)
if (!params.alreadyFalling) {
this.positionY = this.positionY - params.parentHeight - size
}
}
}

fun update() {
positionX += speedX
positionY += speedY

if (positionY > params.parentHeight) {
reset(positionY = -size.toDouble())
}

if (params.fadingEnabled) {
paint.alpha = (alpha * ((params.parentHeight - positionY).toFloat() / params.parentHeight)).toInt()
}
}

fun draw(canvas: Canvas) {
if (bitmap != null) {
canvas.drawBitmap(bitmap, positionX.toFloat(), positionY.toFloat(), paint)
} else {
canvas.drawCircle(positionX.toFloat(), positionY.toFloat(), size.toFloat(), paint)
}
}

data class Params(
val parentWidth: Int,
val parentHeight: Int,
val image: Bitmap?,
val alphaMin: Int,
val alphaMax: Int,
val angleMax: Int,
val sizeMinInPx: Int,
val sizeMaxInPx: Int,
val speedMin: Int,
val speedMax: Int,
val fadingEnabled: Boolean,
val alreadyFalling: Boolean)
}
Loading

0 comments on commit 14f0437

Please sign in to comment.