commit
11237d2009
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<gradient
|
||||
android:startColor="@android:color/transparent"
|
||||
android:endColor="#000000"
|
||||
android:angle="270" />
|
||||
|
||||
</shape>
|
@ -0,0 +1,18 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="17dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="17">
|
||||
<path
|
||||
android:pathData="M11.7187,8.2031C12.4511,8.2031 13.1347,8.02 13.7695,7.6538C14.4043,7.2876 14.9047,6.7871 15.271,6.1523C15.6372,5.5176 15.8203,4.834 15.8203,4.1016C15.8203,3.3691 15.6372,2.6855 15.271,2.0508C14.9047,1.416 14.4043,0.9155 13.7695,0.5493C13.1347,0.1831 12.4511,0 11.7187,0C10.9863,0 10.3027,0.1831 9.6679,0.5493C9.0332,0.9155 8.5327,1.416 8.1665,2.0508C7.8003,2.6855 7.6172,3.3691 7.6172,4.1016C7.6172,4.834 7.8003,5.5176 8.1665,6.1523C8.5327,6.7871 9.0332,7.2876 9.6679,7.6538C10.3027,8.02 10.9863,8.2031 11.7187,8.2031ZM19.9218,7.0312C20.7275,7.0312 21.4172,6.7444 21.9909,6.1706C22.5646,5.5969 22.8515,4.9072 22.8515,4.1016C22.8515,3.2959 22.5646,2.6062 21.9909,2.0325C21.4172,1.4587 20.7275,1.1719 19.9218,1.1719C19.1162,1.1719 18.4265,1.4587 17.8527,2.0325C17.279,2.6062 16.9921,3.2959 16.9921,4.1016C16.9921,4.9072 17.279,5.5969 17.8527,6.1706C18.4265,6.7444 19.1162,7.0312 19.9218,7.0312ZM3.5156,7.0312C4.3213,7.0312 5.011,6.7444 5.5847,6.1706C6.1584,5.5969 6.4453,4.9072 6.4453,4.1016C6.4453,3.2959 6.1584,2.6062 5.5847,2.0325C5.011,1.4587 4.3213,1.1719 3.5156,1.1719C2.71,1.1719 2.0203,1.4587 1.4465,2.0325C0.8728,2.6062 0.5859,3.2959 0.5859,4.1016C0.5859,4.9072 0.8728,5.5969 1.4465,6.1706C2.0203,6.7444 2.71,7.0312 3.5156,7.0312ZM11.7187,6.4453C11.084,6.4453 10.5346,6.2134 10.0708,5.7495C9.6069,5.2856 9.375,4.7363 9.375,4.1016C9.375,3.4668 9.6069,2.9175 10.0708,2.4536C10.5346,1.9897 11.084,1.7578 11.7187,1.7578C12.3535,1.7578 12.9028,1.9897 13.3667,2.4536C13.8305,2.9175 14.0625,3.4668 14.0625,4.1016C14.0625,4.7363 13.8305,5.2856 13.3667,5.7495C12.9028,6.2134 12.3535,6.4453 11.7187,6.4453ZM19.9218,5.2734C19.6044,5.2734 19.3298,5.1575 19.0979,4.9255C18.8659,4.6936 18.75,4.4189 18.75,4.1016C18.75,3.7842 18.8659,3.5095 19.0979,3.2776C19.3298,3.0456 19.6044,2.9297 19.9218,2.9297C20.2392,2.9297 20.5139,3.0456 20.7458,3.2776C20.9777,3.5095 21.0937,3.7842 21.0937,4.1016C21.0937,4.4189 20.9777,4.6936 20.7458,4.9255C20.5139,5.1575 20.2392,5.2734 19.9218,5.2734ZM3.5156,5.2734C3.1982,5.2734 2.9236,5.1575 2.6916,4.9255C2.4597,4.6936 2.3437,4.4189 2.3437,4.1016C2.3437,3.7842 2.4597,3.5095 2.6916,3.2776C2.9236,3.0456 3.1982,2.9297 3.5156,2.9297C3.833,2.9297 4.1077,3.0456 4.3396,3.2776C4.5715,3.5095 4.6875,3.7842 4.6875,4.1016C4.6875,4.4189 4.5715,4.6936 4.3396,4.9255C4.1077,5.1575 3.833,5.2734 3.5156,5.2734ZM22.5585,12.1582C22.8027,12.1582 23.0102,12.0727 23.1811,11.9018C23.352,11.7309 23.4374,11.5234 23.4374,11.2793C23.4374,10.4248 23.1384,9.6985 22.5402,9.1003C21.9421,8.5022 21.2158,8.2031 20.3613,8.2031L20.3613,8.2031L19.4824,8.2031C18.8476,8.2031 18.2739,8.374 17.7612,8.7158C18.2739,9.1064 18.7011,9.5459 19.0429,10.0342C19.2138,9.9853 19.3603,9.9609 19.4824,9.9609L19.4824,9.9609L20.3613,9.9609C20.7275,9.9609 21.0388,10.0891 21.2951,10.3454C21.5515,10.6018 21.6796,10.9131 21.6796,11.2793C21.6796,11.5234 21.7651,11.7309 21.936,11.9018C22.1069,12.0727 22.3144,12.1582 22.5585,12.1582ZM0.8789,12.1582C1.123,12.1582 1.3306,12.0727 1.5015,11.9018C1.6724,11.7309 1.7578,11.5234 1.7578,11.2793C1.7578,10.9131 1.886,10.6018 2.1423,10.3454C2.3987,10.0891 2.71,9.9609 3.0762,9.9609L3.0762,9.9609L3.9551,9.9609C4.1016,9.9609 4.248,9.9975 4.3945,10.0708C4.7363,9.5581 5.1636,9.1064 5.6763,8.7158C5.1636,8.374 4.5898,8.2031 3.9551,8.2031L3.9551,8.2031L3.0762,8.2031C2.2217,8.2031 1.4954,8.5022 0.8972,9.1003C0.2991,9.6985 0,10.4248 0,11.2793C0,11.5234 0.0854,11.7309 0.2563,11.9018C0.4272,12.0727 0.6348,12.1582 0.8789,12.1582ZM16.9921,16.4062C17.4804,16.4062 17.8955,16.2353 18.2373,15.8935C18.5791,15.5517 18.75,15.1367 18.75,14.6484L18.75,14.6484L18.75,13.0005C18.75,12.146 18.5058,11.3647 18.0175,10.6567C17.5293,9.9243 16.8518,9.4177 15.9851,9.1369C15.1184,8.8562 14.2456,8.8501 13.3667,9.1186C12.8051,9.2895 12.2497,9.375 11.7004,9.375C11.1511,9.375 10.6079,9.2895 10.0708,9.1186C9.1919,8.8501 8.3191,8.8562 7.4524,9.1369C6.5857,9.4177 5.9082,9.9182 5.4199,10.6384C4.9316,11.3586 4.6875,12.146 4.6875,13.0005L4.6875,13.0005L4.6875,14.6484C4.6875,15.1367 4.8584,15.5517 5.2002,15.8935C5.542,16.2353 5.957,16.4062 6.4453,16.4062L6.4453,16.4062L16.9921,16.4062ZM16.9921,14.6484L6.4453,14.6484L6.4453,13.0005C6.4453,12.3413 6.6772,11.7737 7.1411,11.2976C7.605,10.8215 8.1665,10.5713 8.8257,10.5469C9.7778,10.9375 10.7422,11.1328 11.7187,11.1328C12.6953,11.1328 13.6596,10.9375 14.6118,10.5469C15.271,10.5713 15.8325,10.8215 16.2963,11.2976C16.7602,11.7737 16.9921,12.3413 16.9921,13.0005L16.9921,13.0005L16.9921,14.6484Z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillType="nonZero"
|
||||
android:strokeColor="#00000000"/>
|
||||
<path
|
||||
android:pathData="M11.7187,8.2031C12.4511,8.2031 13.1347,8.02 13.7695,7.6538C14.4043,7.2876 14.9047,6.7871 15.271,6.1523C15.6372,5.5176 15.8203,4.834 15.8203,4.1016C15.8203,3.3691 15.6372,2.6855 15.271,2.0508C14.9047,1.416 14.4043,0.9155 13.7695,0.5493C13.1347,0.1831 12.4511,0 11.7187,0C10.9863,0 10.3027,0.1831 9.6679,0.5493C9.0332,0.9155 8.5327,1.416 8.1665,2.0508C7.8003,2.6855 7.6172,3.3691 7.6172,4.1016C7.6172,4.834 7.8003,5.5176 8.1665,6.1523C8.5327,6.7871 9.0332,7.2876 9.6679,7.6538C10.3027,8.02 10.9863,8.2031 11.7187,8.2031ZM19.9218,7.0312C20.7275,7.0312 21.4172,6.7444 21.9909,6.1706C22.5646,5.5969 22.8515,4.9072 22.8515,4.1016C22.8515,3.2959 22.5646,2.6062 21.9909,2.0325C21.4172,1.4587 20.7275,1.1719 19.9218,1.1719C19.1162,1.1719 18.4265,1.4587 17.8527,2.0325C17.279,2.6062 16.9921,3.2959 16.9921,4.1016C16.9921,4.9072 17.279,5.5969 17.8527,6.1706C18.4265,6.7444 19.1162,7.0312 19.9218,7.0312ZM3.5156,7.0312C4.3213,7.0312 5.011,6.7444 5.5847,6.1706C6.1584,5.5969 6.4453,4.9072 6.4453,4.1016C6.4453,3.2959 6.1584,2.6062 5.5847,2.0325C5.011,1.4587 4.3213,1.1719 3.5156,1.1719C2.71,1.1719 2.0203,1.4587 1.4465,2.0325C0.8728,2.6062 0.5859,3.2959 0.5859,4.1016C0.5859,4.9072 0.8728,5.5969 1.4465,6.1706C2.0203,6.7444 2.71,7.0312 3.5156,7.0312ZM11.7187,6.4453C11.084,6.4453 10.5346,6.2134 10.0708,5.7495C9.6069,5.2856 9.375,4.7363 9.375,4.1016C9.375,3.4668 9.6069,2.9175 10.0708,2.4536C10.5346,1.9897 11.084,1.7578 11.7187,1.7578C12.3535,1.7578 12.9028,1.9897 13.3667,2.4536C13.8305,2.9175 14.0625,3.4668 14.0625,4.1016C14.0625,4.7363 13.8305,5.2856 13.3667,5.7495C12.9028,6.2134 12.3535,6.4453 11.7187,6.4453ZM19.9218,5.2734C19.6044,5.2734 19.3298,5.1575 19.0979,4.9255C18.8659,4.6936 18.75,4.4189 18.75,4.1016C18.75,3.7842 18.8659,3.5095 19.0979,3.2776C19.3298,3.0456 19.6044,2.9297 19.9218,2.9297C20.2392,2.9297 20.5139,3.0456 20.7458,3.2776C20.9777,3.5095 21.0937,3.7842 21.0937,4.1016C21.0937,4.4189 20.9777,4.6936 20.7458,4.9255C20.5139,5.1575 20.2392,5.2734 19.9218,5.2734ZM3.5156,5.2734C3.1982,5.2734 2.9236,5.1575 2.6916,4.9255C2.4597,4.6936 2.3437,4.4189 2.3437,4.1016C2.3437,3.7842 2.4597,3.5095 2.6916,3.2776C2.9236,3.0456 3.1982,2.9297 3.5156,2.9297C3.833,2.9297 4.1077,3.0456 4.3396,3.2776C4.5715,3.5095 4.6875,3.7842 4.6875,4.1016C4.6875,4.4189 4.5715,4.6936 4.3396,4.9255C4.1077,5.1575 3.833,5.2734 3.5156,5.2734ZM22.5585,12.1582C22.8027,12.1582 23.0102,12.0727 23.1811,11.9018C23.352,11.7309 23.4374,11.5234 23.4374,11.2793C23.4374,10.4248 23.1384,9.6985 22.5402,9.1003C21.9421,8.5022 21.2158,8.2031 20.3613,8.2031L20.3613,8.2031L19.4824,8.2031C18.8476,8.2031 18.2739,8.374 17.7612,8.7158C18.2739,9.1064 18.7011,9.5459 19.0429,10.0342C19.2138,9.9853 19.3603,9.9609 19.4824,9.9609L19.4824,9.9609L20.3613,9.9609C20.7275,9.9609 21.0388,10.0891 21.2951,10.3454C21.5515,10.6018 21.6796,10.9131 21.6796,11.2793C21.6796,11.5234 21.7651,11.7309 21.936,11.9018C22.1069,12.0727 22.3144,12.1582 22.5585,12.1582ZM0.8789,12.1582C1.123,12.1582 1.3306,12.0727 1.5015,11.9018C1.6724,11.7309 1.7578,11.5234 1.7578,11.2793C1.7578,10.9131 1.886,10.6018 2.1423,10.3454C2.3987,10.0891 2.71,9.9609 3.0762,9.9609L3.0762,9.9609L3.9551,9.9609C4.1016,9.9609 4.248,9.9975 4.3945,10.0708C4.7363,9.5581 5.1636,9.1064 5.6763,8.7158C5.1636,8.374 4.5898,8.2031 3.9551,8.2031L3.9551,8.2031L3.0762,8.2031C2.2217,8.2031 1.4954,8.5022 0.8972,9.1003C0.2991,9.6985 0,10.4248 0,11.2793C0,11.5234 0.0854,11.7309 0.2563,11.9018C0.4272,12.0727 0.6348,12.1582 0.8789,12.1582ZM16.9921,16.4062C17.4804,16.4062 17.8955,16.2353 18.2373,15.8935C18.5791,15.5517 18.75,15.1367 18.75,14.6484L18.75,14.6484L18.75,13.0005C18.75,12.146 18.5058,11.3647 18.0175,10.6567C17.5293,9.9243 16.8518,9.4177 15.9851,9.1369C15.1184,8.8562 14.2456,8.8501 13.3667,9.1186C12.8051,9.2895 12.2497,9.375 11.7004,9.375C11.1511,9.375 10.6079,9.2895 10.0708,9.1186C9.1919,8.8501 8.3191,8.8562 7.4524,9.1369C6.5857,9.4177 5.9082,9.9182 5.4199,10.6384C4.9316,11.3586 4.6875,12.146 4.6875,13.0005L4.6875,13.0005L4.6875,14.6484C4.6875,15.1367 4.8584,15.5517 5.2002,15.8935C5.542,16.2353 5.957,16.4062 6.4453,16.4062L6.4453,16.4062L16.9921,16.4062ZM16.9921,14.6484L6.4453,14.6484L6.4453,13.0005C6.4453,12.3413 6.6772,11.7737 7.1411,11.2976C7.605,10.8215 8.1665,10.5713 8.8257,10.5469C9.7778,10.9375 10.7422,11.1328 11.7187,11.1328C12.6953,11.1328 13.6596,10.9375 14.6118,10.5469C15.271,10.5713 15.8325,10.8215 16.2963,11.2976C16.7602,11.7737 16.9921,12.3413 16.9921,13.0005L16.9921,13.0005L16.9921,14.6484Z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillType="nonZero"
|
||||
android:strokeColor="#00000000"/>
|
||||
</vector>
|
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="20">
|
||||
<path
|
||||
android:pathData="M4.2929,14.2929C4.4804,14.1054 4.7348,14 5,14L17,14C17.5523,14 18,13.5523 18,13L18,3C18,2.4477 17.5523,2 17,2L3,2C2.4477,2 2,2.4477 2,3L2,16.5858L4.2929,14.2929ZM5.4142,16L1.7071,19.7071C1.0771,20.3371 0,19.8909 0,19L0,3C0,1.3431 1.3431,0 3,0L17,0C18.6569,0 20,1.3431 20,3L20,13C20,14.6569 18.6569,16 17,16L5.4142,16Z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillType="nonZero"
|
||||
android:strokeColor="#ffffff"/>
|
||||
</vector>
|
@ -0,0 +1,18 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="23dp"
|
||||
android:height="23dp"
|
||||
android:viewportWidth="23"
|
||||
android:viewportHeight="23">
|
||||
<path
|
||||
android:pathData="M11.4167,0.1111L11.4167,0.1111A1.5278,1.5278 0,0 1,12.9444 1.6389L12.9444,20.5833A1.5278,1.5278 0,0 1,11.4167 22.1111L11.4167,22.1111A1.5278,1.5278 0,0 1,9.8889 20.5833L9.8889,1.6389A1.5278,1.5278 0,0 1,11.4167 0.1111z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#00000000"/>
|
||||
<path
|
||||
android:pathData="M22.4167,11.1111C22.4167,11.9549 21.7327,12.6389 20.8889,12.6389L1.9444,12.6389C1.1007,12.6389 0.4167,11.9549 0.4167,11.1111C0.4167,10.2673 1.1007,9.5833 1.9444,9.5833L20.8889,9.5833C21.7327,9.5833 22.4167,10.2673 22.4167,11.1111Z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#00000000"/>
|
||||
</vector>
|
@ -1,7 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<gradient android:type="radial" android:gradientRadius="36dp" android:startColor="@color/accent" android:endColor="@android:color/transparent" />
|
||||
</shape>
|
||||
android:shape="oval" />
|
@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
|
||||
<solid android:color="@color/accent" />
|
||||
</shape>
|
@ -0,0 +1,32 @@
|
||||
package org.thoughtcrime.securesms.loki.redesign.utilities
|
||||
|
||||
import android.graphics.PointF
|
||||
import android.view.View
|
||||
|
||||
fun PointF.distanceTo(other: PointF): Float {
|
||||
return Math.sqrt(Math.pow(this.x.toDouble() - other.x.toDouble(), 2.toDouble()) + Math.pow(this.y.toDouble() - other.y.toDouble(), 2.toDouble())).toFloat()
|
||||
}
|
||||
|
||||
fun PointF.isLeftOf(view: View, margin: Float = 0.0f): Boolean {
|
||||
return isContainedVerticallyIn(view, margin) && x < view.hitRect.left
|
||||
}
|
||||
|
||||
fun PointF.isAbove(view: View, margin: Float = 0.0f): Boolean {
|
||||
return isContainedHorizontallyIn(view, margin) && y < view.hitRect.top
|
||||
}
|
||||
|
||||
fun PointF.isRightOf(view: View, margin: Float = 0.0f): Boolean {
|
||||
return isContainedVerticallyIn(view, margin) && x > view.hitRect.right
|
||||
}
|
||||
|
||||
fun PointF.isBelow(view: View, margin: Float = 0.0f): Boolean {
|
||||
return isContainedHorizontallyIn(view, margin) && y > view.hitRect.bottom
|
||||
}
|
||||
|
||||
fun PointF.isContainedHorizontallyIn(view: View, margin: Float = 0.0f): Boolean {
|
||||
return x >= view.hitRect.left - margin || x <= view.hitRect.right + margin
|
||||
}
|
||||
|
||||
fun PointF.isContainedVerticallyIn(view: View, margin: Float = 0.0f): Boolean {
|
||||
return y >= view.hitRect.top - margin || x <= view.hitRect.bottom + margin
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package org.thoughtcrime.securesms.loki.redesign.utilities
|
||||
|
||||
import android.view.ViewGroup
|
||||
|
||||
fun ViewGroup.disableClipping() {
|
||||
clipToPadding = false
|
||||
clipChildren = false
|
||||
clipToOutline = false
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package org.thoughtcrime.securesms.loki.redesign.utilities
|
||||
|
||||
import android.graphics.PointF
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
|
||||
fun View.contains(point: PointF): Boolean {
|
||||
return hitRect.contains(point.x.toInt(), point.y.toInt())
|
||||
}
|
||||
|
||||
val View.hitRect: Rect
|
||||
get() {
|
||||
val rect = Rect()
|
||||
getHitRect(rect)
|
||||
return rect
|
||||
}
|
@ -0,0 +1,296 @@
|
||||
package org.thoughtcrime.securesms.loki.redesign.views
|
||||
|
||||
import android.animation.ArgbEvaluator
|
||||
import android.animation.FloatEvaluator
|
||||
import android.animation.PointFEvaluator
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.Context
|
||||
import android.content.Context.VIBRATOR_SERVICE
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.PointF
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.os.Build
|
||||
import android.os.VibrationEffect
|
||||
import android.os.VibrationEffect.DEFAULT_AMPLITUDE
|
||||
import android.os.Vibrator
|
||||
import android.support.annotation.ColorRes
|
||||
import android.support.annotation.DimenRes
|
||||
import android.support.annotation.DrawableRes
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.widget.ImageView
|
||||
import android.widget.RelativeLayout
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.loki.getColorWithID
|
||||
import org.thoughtcrime.securesms.loki.redesign.utilities.*
|
||||
import org.thoughtcrime.securesms.loki.toPx
|
||||
|
||||
class NewConversationButtonSetView : RelativeLayout {
|
||||
private var expandedButton: Button? = null
|
||||
private var previousAction: Int? = null
|
||||
private var isExpanded = false
|
||||
var delegate: NewConversationButtonSetViewDelegate? = null
|
||||
|
||||
// region Convenience
|
||||
private val sessionButtonExpandedPosition: PointF get() { return PointF(width.toFloat() / 2 - sessionButton.expandedSize / 2, 0.0f) }
|
||||
private val closedGroupButtonExpandedPosition: PointF get() { return PointF(width.toFloat() - closedGroupButton.expandedSize, height.toFloat() - bottomMargin - closedGroupButton.expandedSize) }
|
||||
private val openGroupButtonExpandedPosition: PointF get() { return PointF(0.0f, height.toFloat() - bottomMargin - openGroupButton.expandedSize) }
|
||||
private val buttonRestPosition: PointF get() { return PointF(width.toFloat() / 2 - mainButton.expandedSize / 2, height.toFloat() - bottomMargin - mainButton.expandedSize) }
|
||||
// endregion
|
||||
|
||||
// region Settings
|
||||
private val maxDragDistance by lazy { toPx(56, resources).toFloat() }
|
||||
private val dragMargin by lazy { toPx(16, resources).toFloat() }
|
||||
private val bottomMargin by lazy { resources.getDimension(R.dimen.new_conversation_button_bottom_offset) }
|
||||
// endregion
|
||||
|
||||
// region Components
|
||||
private val mainButton by lazy { Button(context, true, R.drawable.ic_plus) }
|
||||
private val sessionButton by lazy { Button(context, false, R.drawable.ic_message) }
|
||||
private val closedGroupButton by lazy { Button(context, false, R.drawable.ic_group) }
|
||||
private val openGroupButton by lazy { Button(context, false, R.drawable.ic_globe) }
|
||||
// endregion
|
||||
|
||||
// region Button
|
||||
class Button : RelativeLayout {
|
||||
@DrawableRes private var iconID = 0
|
||||
private var isMain = false
|
||||
|
||||
companion object {
|
||||
val animationDuration = 250.toLong()
|
||||
}
|
||||
|
||||
val expandedSize by lazy { resources.getDimension(R.dimen.new_conversation_button_expanded_size) }
|
||||
val collapsedSize by lazy { resources.getDimension(R.dimen.new_conversation_button_collapsed_size) }
|
||||
private val expandedImageViewPosition by lazy { PointF(0.0f, 0.0f) }
|
||||
private val collapsedImageViewPosition by lazy { PointF((expandedSize - collapsedSize) / 2, (expandedSize - collapsedSize) / 2) }
|
||||
|
||||
private val imageView by lazy {
|
||||
val result = ImageView(context)
|
||||
val size = collapsedSize.toInt()
|
||||
result.layoutParams = LayoutParams(size, size)
|
||||
result.setBackgroundResource(R.drawable.new_conversation_button_background)
|
||||
val background = result.background as GradientDrawable
|
||||
val colorID = if (isMain) R.color.accent else R.color.new_conversation_button_collapsed_background
|
||||
background.color = ColorStateList.valueOf(resources.getColorWithID(colorID, context.theme))
|
||||
result.scaleType = ImageView.ScaleType.CENTER
|
||||
result.setImageResource(iconID)
|
||||
result
|
||||
}
|
||||
|
||||
constructor(context: Context) : super(context) { throw IllegalAccessException("Use Button(context:iconID:) instead.") }
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { throw IllegalAccessException("Use Button(context:iconID:) instead.") }
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { throw IllegalAccessException("Use Button(context:iconID:) instead.") }
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { throw IllegalAccessException("Use Button(context:iconID:) instead.") }
|
||||
|
||||
constructor(context: Context, isMain: Boolean, @DrawableRes iconID: Int) : super(context) {
|
||||
this.iconID = iconID
|
||||
this.isMain = isMain
|
||||
disableClipping()
|
||||
val size = resources.getDimension(R.dimen.new_conversation_button_expanded_size).toInt()
|
||||
val layoutParams = LayoutParams(size, size)
|
||||
this.layoutParams = layoutParams
|
||||
addView(imageView)
|
||||
imageView.x = collapsedImageViewPosition.x
|
||||
imageView.y = collapsedImageViewPosition.y
|
||||
}
|
||||
|
||||
fun expand() {
|
||||
animateImageViewColorChange(R.color.new_conversation_button_collapsed_background, R.color.accent)
|
||||
animateImageViewSizeChange(R.dimen.new_conversation_button_collapsed_size, R.dimen.new_conversation_button_expanded_size)
|
||||
animateImageViewPositionChange(collapsedImageViewPosition, expandedImageViewPosition)
|
||||
}
|
||||
|
||||
fun collapse() {
|
||||
animateImageViewColorChange(R.color.accent, R.color.new_conversation_button_collapsed_background)
|
||||
animateImageViewSizeChange(R.dimen.new_conversation_button_expanded_size, R.dimen.new_conversation_button_collapsed_size)
|
||||
animateImageViewPositionChange(expandedImageViewPosition, collapsedImageViewPosition)
|
||||
}
|
||||
|
||||
private fun animateImageViewColorChange(@ColorRes startColorID: Int, @ColorRes endColorID: Int) {
|
||||
val drawable = imageView.background as GradientDrawable
|
||||
val startColor = resources.getColorWithID(startColorID, context.theme)
|
||||
val endColor = resources.getColorWithID(endColorID, context.theme)
|
||||
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
|
||||
animation.duration = animationDuration
|
||||
animation.addUpdateListener { animator ->
|
||||
val color = animator.animatedValue as Int
|
||||
drawable.color = ColorStateList.valueOf(color)
|
||||
}
|
||||
animation.start()
|
||||
}
|
||||
|
||||
private fun animateImageViewSizeChange(@DimenRes startSizeID: Int, @DimenRes endSizeID: Int) {
|
||||
val layoutParams = imageView.layoutParams
|
||||
val startSize = resources.getDimension(startSizeID)
|
||||
val endSize = resources.getDimension(endSizeID)
|
||||
val animation = ValueAnimator.ofObject(FloatEvaluator(), startSize, endSize)
|
||||
animation.duration = animationDuration
|
||||
animation.addUpdateListener { animator ->
|
||||
val size = animator.animatedValue as Float
|
||||
layoutParams.width = size.toInt()
|
||||
layoutParams.height = size.toInt()
|
||||
imageView.layoutParams = layoutParams
|
||||
}
|
||||
animation.start()
|
||||
}
|
||||
|
||||
private fun animateImageViewPositionChange(startPosition: PointF, endPosition: PointF) {
|
||||
val animation = ValueAnimator.ofObject(PointFEvaluator(), startPosition, endPosition)
|
||||
animation.duration = animationDuration
|
||||
animation.addUpdateListener { animator ->
|
||||
val point = animator.animatedValue as PointF
|
||||
imageView.x = point.x
|
||||
imageView.y = point.y
|
||||
}
|
||||
animation.start()
|
||||
}
|
||||
|
||||
fun animatePositionChange(startPosition: PointF, endPosition: PointF) {
|
||||
val animation = ValueAnimator.ofObject(PointFEvaluator(), startPosition, endPosition)
|
||||
animation.duration = animationDuration
|
||||
animation.addUpdateListener { animator ->
|
||||
val point = animator.animatedValue as PointF
|
||||
x = point.x
|
||||
y = point.y
|
||||
}
|
||||
animation.start()
|
||||
}
|
||||
|
||||
fun animateAlphaChange(startAlpha: Float, endAlpha: Float) {
|
||||
val animation = ValueAnimator.ofObject(FloatEvaluator(), startAlpha, endAlpha)
|
||||
animation.duration = animationDuration
|
||||
animation.addUpdateListener { animator ->
|
||||
alpha = animator.animatedValue as Float
|
||||
}
|
||||
animation.start()
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Lifecycle
|
||||
constructor(context: Context) : super(context) { setUpViewHierarchy() }
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { setUpViewHierarchy() }
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { setUpViewHierarchy() }
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { setUpViewHierarchy() }
|
||||
|
||||
private fun setUpViewHierarchy() {
|
||||
// Set up session button
|
||||
addView(sessionButton)
|
||||
sessionButton.alpha = 0.0f
|
||||
val sessionButtonLayoutParams = sessionButton.layoutParams as LayoutParams
|
||||
sessionButtonLayoutParams.addRule(CENTER_IN_PARENT, TRUE)
|
||||
sessionButtonLayoutParams.addRule(ALIGN_PARENT_BOTTOM, TRUE)
|
||||
sessionButtonLayoutParams.bottomMargin = bottomMargin.toInt()
|
||||
// Set up closed group button
|
||||
addView(closedGroupButton)
|
||||
closedGroupButton.alpha = 0.0f
|
||||
val closedGroupButtonLayoutParams = closedGroupButton.layoutParams as LayoutParams
|
||||
closedGroupButtonLayoutParams.addRule(CENTER_IN_PARENT, TRUE)
|
||||
closedGroupButtonLayoutParams.addRule(ALIGN_PARENT_BOTTOM, TRUE)
|
||||
closedGroupButtonLayoutParams.bottomMargin = bottomMargin.toInt()
|
||||
// Set up open group button
|
||||
addView(openGroupButton)
|
||||
openGroupButton.alpha = 0.0f
|
||||
val openGroupButtonLayoutParams = openGroupButton.layoutParams as LayoutParams
|
||||
openGroupButtonLayoutParams.addRule(CENTER_IN_PARENT, TRUE)
|
||||
openGroupButtonLayoutParams.addRule(ALIGN_PARENT_BOTTOM, TRUE)
|
||||
openGroupButtonLayoutParams.bottomMargin = bottomMargin.toInt()
|
||||
// Set up main button
|
||||
addView(mainButton)
|
||||
val mainButtonLayoutParams = mainButton.layoutParams as LayoutParams
|
||||
mainButtonLayoutParams.addRule(CENTER_IN_PARENT, TRUE)
|
||||
mainButtonLayoutParams.addRule(ALIGN_PARENT_BOTTOM, TRUE)
|
||||
mainButtonLayoutParams.bottomMargin = bottomMargin.toInt()
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Interaction
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
val touch = PointF(event.x, event.y)
|
||||
val expandedButton = expandedButton
|
||||
val allButtons = listOf( mainButton, sessionButton, closedGroupButton, openGroupButton )
|
||||
val buttonsExcludingMainButton = listOf( sessionButton, closedGroupButton, openGroupButton )
|
||||
if (allButtons.none { it.contains(touch) }) { return false }
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
val vibrator = context.getSystemService(VIBRATOR_SERVICE) as Vibrator
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
vibrator.vibrate(VibrationEffect.createOneShot(50, DEFAULT_AMPLITUDE))
|
||||
} else {
|
||||
vibrator.vibrate(50)
|
||||
}
|
||||
if (!isExpanded && mainButton.contains(touch)) {
|
||||
expand()
|
||||
} else if (buttonsExcludingMainButton.none { it.contains(touch) }) {
|
||||
collapse()
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
mainButton.x = touch.x - mainButton.expandedSize / 2
|
||||
mainButton.y = touch.y - mainButton.expandedSize / 2
|
||||
mainButton.alpha = 1 - (PointF(mainButton.x, mainButton.y).distanceTo(buttonRestPosition) / maxDragDistance)
|
||||
val buttonToExpand = buttonsExcludingMainButton.firstOrNull { button ->
|
||||
var hasUserDraggedBeyondButton = false
|
||||
if (button == openGroupButton && touch.isLeftOf(openGroupButton, dragMargin)) { hasUserDraggedBeyondButton = true }
|
||||
if (button == sessionButton && touch.isAbove(sessionButton, dragMargin)) { hasUserDraggedBeyondButton = true }
|
||||
if (button == closedGroupButton && touch.isRightOf(closedGroupButton, dragMargin)) { hasUserDraggedBeyondButton = true }
|
||||
button.contains(touch) || hasUserDraggedBeyondButton
|
||||
}
|
||||
if (buttonToExpand != null) {
|
||||
if (buttonToExpand == expandedButton) { return true }
|
||||
expandedButton?.collapse()
|
||||
buttonToExpand.expand()
|
||||
this.expandedButton = buttonToExpand
|
||||
} else {
|
||||
expandedButton?.collapse()
|
||||
this.expandedButton = null
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||
if (previousAction == MotionEvent.ACTION_MOVE || isExpanded) {
|
||||
expandedButton?.collapse()
|
||||
this.expandedButton = null
|
||||
collapse()
|
||||
if (event.action == MotionEvent.ACTION_UP) {
|
||||
if (sessionButton.contains(touch) || touch.isAbove(sessionButton, dragMargin)) { delegate?.createNewPrivateChat() }
|
||||
else if (closedGroupButton.contains(touch) || touch.isRightOf(closedGroupButton, dragMargin)) { delegate?.createNewClosedGroup() }
|
||||
else if (openGroupButton.contains(touch) || touch.isLeftOf(openGroupButton, dragMargin)) { delegate?.joinOpenGroup() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
previousAction = event.action
|
||||
return true
|
||||
}
|
||||
|
||||
private fun expand() {
|
||||
val buttonsExcludingMainButton = listOf( sessionButton, closedGroupButton, openGroupButton )
|
||||
sessionButton.animatePositionChange(buttonRestPosition, sessionButtonExpandedPosition)
|
||||
closedGroupButton.animatePositionChange(buttonRestPosition, closedGroupButtonExpandedPosition)
|
||||
openGroupButton.animatePositionChange(buttonRestPosition, openGroupButtonExpandedPosition)
|
||||
buttonsExcludingMainButton.forEach { it.animateAlphaChange(0.0f, 1.0f) }
|
||||
postDelayed({ isExpanded = true }, Button.animationDuration)
|
||||
}
|
||||
|
||||
private fun collapse() {
|
||||
val allButtons = listOf( mainButton, sessionButton, closedGroupButton, openGroupButton )
|
||||
allButtons.forEach {
|
||||
val currentPosition = PointF(it.x, it.y)
|
||||
it.animatePositionChange(currentPosition, buttonRestPosition)
|
||||
val endAlpha = if (it == mainButton) 1.0f else 0.0f
|
||||
it.animateAlphaChange(it.alpha, endAlpha)
|
||||
}
|
||||
postDelayed({ isExpanded = false }, Button.animationDuration)
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
||||
// region Delegate
|
||||
interface NewConversationButtonSetViewDelegate {
|
||||
|
||||
fun joinOpenGroup()
|
||||
fun createNewPrivateChat()
|
||||
fun createNewClosedGroup()
|
||||
}
|
||||
// endregion
|
Loading…
Reference in New Issue