Add touch interaction for the SplashFragment art
This commit is contained in:
@@ -9,8 +9,8 @@ android {
|
|||||||
applicationId "org.joinmastodon.android"
|
applicationId "org.joinmastodon.android"
|
||||||
minSdk 23
|
minSdk 23
|
||||||
targetSdk 33
|
targetSdk 33
|
||||||
versionCode 52
|
versionCode 53
|
||||||
versionName "1.2.1"
|
versionName "1.2.2"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
resConfigs "ar-rSA", "be-rBY", "bn-rBD", "bs-rBA", "ca-rES", "cs-rCZ", "da-rDK", "de-rDE", "el-rGR", "es-rES", "eu-rES", "fa-rIR", "fi-rFI", "fil-rPH", "fr-rFR", "ga-rIE", "gd-rGB", "gl-rES", "hi-rIN", "hr-rHR", "hu-rHU", "hy-rAM", "ig-rNG", "in-rID", "is-rIS", "it-rIT", "iw-rIL", "ja-rJP", "kab", "ko-rKR", "my-rMM", "nl-rNL", "no-rNO", "oc-rFR", "pl-rPL", "pt-rBR", "pt-rPT", "ro-rRO", "ru-rRU", "si-rLK", "sl-rSI", "sv-rSE", "th-rTH", "tr-rTR", "uk-rUA", "ur-rIN", "vi-rVN", "zh-rCN", "zh-rTW"
|
resConfigs "ar-rSA", "be-rBY", "bn-rBD", "bs-rBA", "ca-rES", "cs-rCZ", "da-rDK", "de-rDE", "el-rGR", "es-rES", "eu-rES", "fa-rIR", "fi-rFI", "fil-rPH", "fr-rFR", "ga-rIE", "gd-rGB", "gl-rES", "hi-rIN", "hr-rHR", "hu-rHU", "hy-rAM", "ig-rNG", "in-rID", "is-rIS", "it-rIT", "iw-rIL", "ja-rJP", "kab", "ko-rKR", "my-rMM", "nl-rNL", "no-rNO", "oc-rFR", "pl-rPL", "pt-rBR", "pt-rPT", "ro-rRO", "ru-rRU", "si-rLK", "sl-rSI", "sv-rSE", "th-rTH", "tr-rTR", "uk-rUA", "ur-rIN", "vi-rVN", "zh-rCN", "zh-rTW"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,13 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.SpannableString;
|
|
||||||
import android.text.style.ReplacementSpan;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.MastodonApp;
|
import org.joinmastodon.android.MastodonApp;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
@@ -28,10 +21,7 @@ import org.joinmastodon.android.ui.utils.UiUtils;
|
|||||||
import org.joinmastodon.android.ui.views.SizeListenerFrameLayout;
|
import org.joinmastodon.android.ui.views.SizeListenerFrameLayout;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
import androidx.viewpager2.widget.ViewPager2;
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
@@ -74,11 +64,12 @@ public class SplashFragment extends AppKitFragment{
|
|||||||
artContainer=contentView.findViewById(R.id.art_container);
|
artContainer=contentView.findViewById(R.id.art_container);
|
||||||
blueFill=contentView.findViewById(R.id.blue_fill);
|
blueFill=contentView.findViewById(R.id.blue_fill);
|
||||||
greenFill=contentView.findViewById(R.id.green_fill);
|
greenFill=contentView.findViewById(R.id.green_fill);
|
||||||
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(contentView.findViewById(R.id.art_clouds), V.dp(-5), V.dp(5), V.dp(-5), V.dp(5)));
|
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(artClouds, V.dp(-5), V.dp(5), V.dp(-5), V.dp(5)));
|
||||||
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(contentView.findViewById(R.id.art_right_hill), V.dp(-15), V.dp(25), V.dp(-10), V.dp(10)));
|
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(artRightHill, V.dp(-15), V.dp(25), V.dp(-10), V.dp(10)));
|
||||||
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(contentView.findViewById(R.id.art_left_hill), V.dp(-25), V.dp(15), V.dp(-15), V.dp(15)));
|
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(artLeftHill, V.dp(-25), V.dp(15), V.dp(-15), V.dp(15)));
|
||||||
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(contentView.findViewById(R.id.art_center_hill), V.dp(-14), V.dp(14), V.dp(-5), V.dp(25)));
|
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(artCenterHill, V.dp(-14), V.dp(14), V.dp(-5), V.dp(25)));
|
||||||
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(contentView.findViewById(R.id.art_plane_elephant), V.dp(-20), V.dp(12), V.dp(-20), V.dp(12)));
|
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(artPlaneElephant, V.dp(-20), V.dp(12), V.dp(-20), V.dp(12)));
|
||||||
|
artContainer.setOnTouchListener(motionEffect);
|
||||||
|
|
||||||
contentView.setSizeListener(new SizeListenerFrameLayout.OnSizeChangedListener(){
|
contentView.setSizeListener(new SizeListenerFrameLayout.OnSizeChangedListener(){
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,17 +1,25 @@
|
|||||||
package org.joinmastodon.android.ui;
|
package org.joinmastodon.android.ui;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.hardware.Sensor;
|
import android.hardware.Sensor;
|
||||||
import android.hardware.SensorEvent;
|
import android.hardware.SensorEvent;
|
||||||
import android.hardware.SensorEventListener;
|
import android.hardware.SensorEventListener;
|
||||||
import android.hardware.SensorManager;
|
import android.hardware.SensorManager;
|
||||||
|
import android.view.MotionEvent;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.view.animation.PathInterpolator;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class InterpolatingMotionEffect implements SensorEventListener{
|
import androidx.dynamicanimation.animation.DynamicAnimation;
|
||||||
|
import androidx.dynamicanimation.animation.FloatValueHolder;
|
||||||
|
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||||
|
import androidx.dynamicanimation.animation.SpringForce;
|
||||||
|
|
||||||
|
public class InterpolatingMotionEffect implements SensorEventListener, View.OnTouchListener{
|
||||||
|
|
||||||
private SensorManager sm;
|
private SensorManager sm;
|
||||||
private WindowManager wm;
|
private WindowManager wm;
|
||||||
@@ -20,6 +28,34 @@ public class InterpolatingMotionEffect implements SensorEventListener{
|
|||||||
private Sensor accelerometer;
|
private Sensor accelerometer;
|
||||||
private boolean accelerometerEnabled;
|
private boolean accelerometerEnabled;
|
||||||
private ArrayList<ViewEffect> views=new ArrayList<>();
|
private ArrayList<ViewEffect> views=new ArrayList<>();
|
||||||
|
private float pitch, roll;
|
||||||
|
private float touchDownX, touchDownY, touchAddX, touchAddY, touchAddLastAnimX, touchAddLastAnimY;
|
||||||
|
private PathInterpolator touchInterpolator=new PathInterpolator(0.5f, 1f, 0.89f, 1f);
|
||||||
|
private SpringAnimation touchSpringX, touchSpringY;
|
||||||
|
private FloatValueHolder touchSpringXHolder=new FloatValueHolder(){
|
||||||
|
@Override
|
||||||
|
public float getValue(){
|
||||||
|
return touchAddX;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setValue(float value){
|
||||||
|
touchAddX=value;
|
||||||
|
updateEffects();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private FloatValueHolder touchSpringYHolder=new FloatValueHolder(){
|
||||||
|
@Override
|
||||||
|
public float getValue(){
|
||||||
|
return touchAddY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setValue(float value){
|
||||||
|
touchAddY=value;
|
||||||
|
updateEffects();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public InterpolatingMotionEffect(Context context){
|
public InterpolatingMotionEffect(Context context){
|
||||||
sm=context.getSystemService(SensorManager.class);
|
sm=context.getSystemService(SensorManager.class);
|
||||||
@@ -50,8 +86,8 @@ public class InterpolatingMotionEffect implements SensorEventListener{
|
|||||||
float z=event.values[2]/SensorManager.GRAVITY_EARTH;
|
float z=event.values[2]/SensorManager.GRAVITY_EARTH;
|
||||||
|
|
||||||
|
|
||||||
float pitch=(float) (Math.atan2(x, Math.sqrt(y*y+z*z))/Math.PI*2.0);
|
pitch=(float) (Math.atan2(x, Math.sqrt(y*y+z*z))/Math.PI*2.0);
|
||||||
float roll=(float) (Math.atan2(y, Math.sqrt(x*x+z*z))/Math.PI*2.0);
|
roll=(float) (Math.atan2(y, Math.sqrt(x*x+z*z))/Math.PI*2.0);
|
||||||
|
|
||||||
switch(rotation){
|
switch(rotation){
|
||||||
case Surface.ROTATION_0:
|
case Surface.ROTATION_0:
|
||||||
@@ -88,9 +124,7 @@ public class InterpolatingMotionEffect implements SensorEventListener{
|
|||||||
}else if(roll<-1f){
|
}else if(roll<-1f){
|
||||||
roll=-2f-roll;
|
roll=-2f-roll;
|
||||||
}
|
}
|
||||||
for(ViewEffect view:views){
|
updateEffects();
|
||||||
view.update(pitch, roll);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -110,6 +144,62 @@ public class InterpolatingMotionEffect implements SensorEventListener{
|
|||||||
views.clear();
|
views.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
@Override
|
||||||
|
public boolean onTouch(View v, MotionEvent ev){
|
||||||
|
switch(ev.getAction()){
|
||||||
|
case MotionEvent.ACTION_DOWN -> {
|
||||||
|
if(touchSpringX!=null){
|
||||||
|
touchAddLastAnimX=touchAddX;
|
||||||
|
touchSpringX.cancel();
|
||||||
|
touchSpringX=null;
|
||||||
|
}else{
|
||||||
|
touchAddLastAnimX=0;
|
||||||
|
}
|
||||||
|
if(touchSpringY!=null){
|
||||||
|
touchAddLastAnimY=touchAddY;
|
||||||
|
touchSpringY.cancel();
|
||||||
|
touchSpringY=null;
|
||||||
|
}else{
|
||||||
|
touchAddLastAnimY=0;
|
||||||
|
}
|
||||||
|
touchDownX=ev.getX();
|
||||||
|
touchDownY=ev.getY();
|
||||||
|
}
|
||||||
|
case MotionEvent.ACTION_MOVE -> {
|
||||||
|
touchAddX=touchInterpolator.getInterpolation(Math.min(1f, Math.abs((ev.getX()-touchDownX)/(v.getWidth()/2f))));
|
||||||
|
touchAddY=touchInterpolator.getInterpolation(Math.min(1f, Math.abs((ev.getY()-touchDownY)/(v.getHeight()/2f))));
|
||||||
|
if(ev.getX()>touchDownX)
|
||||||
|
touchAddX=-touchAddX;
|
||||||
|
if(ev.getY()<touchDownY)
|
||||||
|
touchAddY=-touchAddY;
|
||||||
|
touchAddX+=touchAddLastAnimX;
|
||||||
|
touchAddY+=touchAddLastAnimY;
|
||||||
|
updateEffects();
|
||||||
|
}
|
||||||
|
case MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||||
|
touchSpringX=new SpringAnimation(touchSpringXHolder, 0f);
|
||||||
|
touchSpringX.setMinimumVisibleChange(0.01f);
|
||||||
|
touchSpringX.getSpring().setStiffness(SpringForce.STIFFNESS_LOW).setDampingRatio(0.85f);
|
||||||
|
touchSpringX.addEndListener((animation, canceled, value, velocity)->touchSpringX=null);
|
||||||
|
touchSpringX.start();
|
||||||
|
touchSpringY=new SpringAnimation(touchSpringYHolder, 0f);
|
||||||
|
touchSpringY.setMinimumVisibleChange(0.01f);
|
||||||
|
touchSpringY.getSpring().setStiffness(SpringForce.STIFFNESS_LOW).setDampingRatio(0.85f);
|
||||||
|
touchSpringY.addEndListener((animation, canceled, value, velocity)->touchSpringY=null);
|
||||||
|
touchSpringY.start();
|
||||||
|
updateEffects();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateEffects(){
|
||||||
|
for(ViewEffect view:views){
|
||||||
|
view.update(Math.min(1f, Math.max(-1f, pitch+touchAddX)), Math.min(1f, Math.max(-1f, roll+touchAddY)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class ViewEffect{
|
public static class ViewEffect{
|
||||||
private View view;
|
private View view;
|
||||||
private float minX, maxX, minY, maxY;
|
private float minX, maxX, minY, maxY;
|
||||||
|
|||||||
Reference in New Issue
Block a user