From 12e973d09ac8fa4ff0240677645391ec40055d79 Mon Sep 17 00:00:00 2001 From: Emre BAYRAKTAR Date: Tue, 30 May 2023 17:17:59 +0300 Subject: [PATCH] Converted FadingTextView file from Java to Kotlin --- app/build.gradle | 6 +- build.gradle | 8 +- fadingtextview/build.gradle | 10 +- .../tomer/fadingtextview/FadingTextView.java | 352 ------------------ .../tomer/fadingtextview/FadingTextView.kt | 191 ++++++++++ gradle/wrapper/gradle-wrapper.properties | 3 +- 6 files changed, 207 insertions(+), 363 deletions(-) delete mode 100644 fadingtextview/src/main/java/com/tomer/fadingtextview/FadingTextView.java create mode 100644 fadingtextview/src/main/java/com/tomer/fadingtextview/FadingTextView.kt diff --git a/app/build.gradle b/app/build.gradle index f7a4817..339252b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,8 +1,8 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 32 - buildToolsVersion '26.0.3' + namespace 'com.tomerrosenfeld.fadingtextview' + compileSdk 32 defaultConfig { applicationId "com.tomerrosenfeld.fadingtextview" minSdkVersion 27 @@ -25,7 +25,7 @@ android { } dependencies { - androidTestCompile('androidx.test.espresso:espresso-core:3.1.0', { + androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', { exclude group: 'com.android.support', module: 'support-annotations' }) implementation project(':fadingtextview') diff --git a/build.gradle b/build.gradle index d6edbf0..0fed351 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,16 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { + ext { + kotlin_version = '1.8.21' + } repositories { jcenter() google() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:7.4.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' } @@ -22,6 +26,6 @@ allprojects { } } -task clean(type: Delete) { +tasks.register('clean', Delete) { delete rootProject.buildDir } \ No newline at end of file diff --git a/fadingtextview/build.gradle b/fadingtextview/build.gradle index 86a73ef..5077953 100644 --- a/fadingtextview/build.gradle +++ b/fadingtextview/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.library' +apply plugin: 'org.jetbrains.kotlin.android' ext { bintrayRepo = 'maven' @@ -25,8 +26,8 @@ ext { } android { - compileSdkVersion 32 - buildToolsVersion "26.0.3" + namespace 'com.tomer.fadingtextview' + compileSdk 32 defaultConfig { minSdkVersion 14 @@ -47,6 +48,5 @@ dependencies { testImplementation 'junit:junit:4.12' } -apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' -apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle' - +//apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' // deprecated +//apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle' // deprecated diff --git a/fadingtextview/src/main/java/com/tomer/fadingtextview/FadingTextView.java b/fadingtextview/src/main/java/com/tomer/fadingtextview/FadingTextView.java deleted file mode 100644 index 3f6614f..0000000 --- a/fadingtextview/src/main/java/com/tomer/fadingtextview/FadingTextView.java +++ /dev/null @@ -1,352 +0,0 @@ -package com.tomer.fadingtextview; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.res.TypedArray; -import android.os.Handler; -import android.util.AttributeSet; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; - -import androidx.annotation.ArrayRes; -import androidx.annotation.IntDef; -import androidx.annotation.NonNull; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -/** - * @author Tomer Rosenfeld AKA rosenpin - * Created by rosenpin on 12/8/16. - */ -public class FadingTextView extends androidx.appcompat.widget.AppCompatTextView { - - public static final int DEFAULT_TIME_OUT = 15000; - public static final int MILLISECONDS = 1, - SECONDS = 2, - MINUTES = 3; - - private Animation fadeInAnimation, fadeOutAnimation; - private Handler handler; - private CharSequence[] texts; - private boolean isShown; - private int position; - private int timeout = DEFAULT_TIME_OUT; - private boolean stopped; - - public FadingTextView(Context context) { - super(context); - init(); - } - - public FadingTextView(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - handleAttrs(attrs); - } - - public FadingTextView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(); - handleAttrs(attrs); - } - - /** - * Resumes the animation - * Should only be used if you notice {@link #onAttachedToWindow()} ()} is not being executed as expected - */ - public void resume() { - isShown = true; - startAnimation(); - } - - /** - * Pauses the animation - * Should only be used if you notice {@link #onDetachedFromWindow()} is not being executed as expected - */ - public void pause() { - isShown = false; - stopAnimation(); - } - - /** - * Stops the animation - * Unlike the pause function, the stop method will permanently stop the animation until the view is restarted - */ - public void stop() { - isShown = false; - stopped = true; - stopAnimation(); - } - - /** - * Restarts the animation - * Only use this to restart the animation after stopping it using {@link #stop} - */ - public void restart() { - isShown = true; - stopped = false; - startAnimation(); - invalidate(); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - pause(); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - resume(); - } - - /** - * Initialize the view and the animations - */ - private void init() { - fadeInAnimation = AnimationUtils.loadAnimation(getContext(), R.anim.fadein); - fadeOutAnimation = AnimationUtils.loadAnimation(getContext(), R.anim.fadeout); - handler = new Handler(); - isShown = true; - } - - /** - * Handle the attributes - * set the texts - * set the timeout - * - * @param attrs provided attributes - */ - private void handleAttrs(AttributeSet attrs) { - TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.FadingTextView); - this.texts = a.getTextArray(R.styleable.FadingTextView_texts); - this.timeout = Math.abs(a.getInteger(R.styleable.FadingTextView_timeout, 14500)) + - getResources().getInteger(android.R.integer.config_longAnimTime); - - boolean shuffle = a.getBoolean(R.styleable.FadingTextView_shuffle, false); - if (shuffle) { - shuffle(); - } - - a.recycle(); - } - - /** - * Get a list of the texts - * - * @return the texts array - */ - public CharSequence[] getTexts() { - return texts; - } - - /** - * Sets the texts to be shuffled using a string array - * - * @param texts The string array to use for the texts - */ - public void setTexts(@NonNull String[] texts) { - if (texts.length < 1) { - throw new IllegalArgumentException("There must be at least one text"); - } else { - this.texts = texts; - stopAnimation(); - position = 0; - startAnimation(); - } - } - - /** - * Sets the texts to be shuffled using a string array resource - * - * @param texts The string array resource to use for the texts - */ - public void setTexts(@ArrayRes int texts) { - if (getResources().getStringArray(texts).length < 1) { - throw new IllegalArgumentException("There must be at least one text"); - } else { - this.texts = getResources().getStringArray(texts); - stopAnimation(); - position = 0; - startAnimation(); - } - } - - /** - * This method should only be used to forcefully apply timeout changes - * It will dismiss the currently queued animation change and start a new animation - */ - public void forceRefresh() { - stopAnimation(); - startAnimation(); - } - - /** - * Fades text to position in provided array and pauses - * Consider calling pause() method before calling this function to avoid overriding currently active animation - */ - public void fadeTo(int position){ - this.position = position; - isShown = true; - startAnimation(); - pause(); - } - - /** - * Shuffle the strings - * Each time this method is ran the order of the strings will be randomized - * After you set texts dynamically you will have to call shuffle again - * - * @throws IllegalArgumentException if you don't supply texts to the FadingTextView in your XML file. You can leave it empty by using FTV.placeholder and set it manually later using the setTexts method - */ - public void shuffle() { - if (this.texts == null) { - throw new IllegalArgumentException("You must provide a string array to the FadingTextView using the texts parameter or use FTV.placeholder to leave it empty"); - } - List texts = Arrays.asList(this.texts); - Collections.shuffle(texts); - this.texts = (CharSequence[]) texts.toArray(); - } - - /** - * Sets the length of time to wait between text changes in milliseconds - * - * @param timeout The length of time to wait between text change in milliseconds - * @deprecated use {@link #setTimeout(long, java.util.concurrent.TimeUnit)} instead. - */ - @Deprecated - public void setTimeout(int timeout) { - if (timeout < 1) { - throw new IllegalArgumentException("Timeout must be longer than 0"); - } else { - this.timeout = timeout; - } - } - - /** - * Sets the length of time to wait between text changes in specific time units - * - * @param timeout The length of time to wait between text change - * @param timeUnit The time unit to use for the timeout parameter - * Must be of {@link TimeUnit} type. Either {@link #MILLISECONDS} or - * {@link #SECONDS} or - * {@link #MINUTES} - * @deprecated use {@link #setTimeout(long, java.util.concurrent.TimeUnit)} instead. - */ - @Deprecated - public void setTimeout(double timeout, @TimeUnit int timeUnit) { - if (timeout <= 0) { - throw new IllegalArgumentException("Timeout must be longer than 0"); - } else { - int multiplier; - switch (timeUnit) { - case MILLISECONDS: - multiplier = 1; - break; - case SECONDS: - multiplier = 1000; - break; - case MINUTES: - multiplier = 60000; - break; - default: - multiplier = 1; - break; - } - this.timeout = (int) (timeout * multiplier); - } - } - - @SuppressLint("reference not found") - /** - * Sets the length of time to wait between text changes in specific time units - * - * @param timeout The length of time to wait between text change - * @param timeUnit The time unit to use for the timeout parameter - * Must be of {@link java.util.concurrent.TimeUnit} type. - * Must be one of - * {@link java.util.concurrent.TimeUnit.NANOSECONDS} or - * {@link java.util.concurrent.TimeUnit.MICROSECONDS} or - * {@link java.util.concurrent.TimeUnit.MILLISECONDS} or - * {@link java.util.concurrent.TimeUnit.SECONDS} or - * {@link java.util.concurrent.TimeUnit.MINUTES} or - * {@link java.util.concurrent.TimeUnit.HOURS} or - * {@link java.util.concurrent.TimeUnit.DAYS} or - */ - public void setTimeout(long timeout, java.util.concurrent.TimeUnit timeUnit) { - if (timeout <= 0) { - throw new IllegalArgumentException("Timeout must be longer than 0"); - } else { - this.timeout = (int) java.util.concurrent.TimeUnit.MILLISECONDS - .convert(timeout, timeUnit); - } - } - - /** - * Start the specified animation now if should - * - * @param animation the animation to start now - */ - @Override - public void startAnimation(Animation animation) { - if (isShown && !stopped) { - super.startAnimation(animation); - } - } - - /** - * Start the animation - */ - protected void startAnimation() { - if (!isInEditMode()) { - setText(texts[position]); - startAnimation(fadeInAnimation); - handler.postDelayed(new Runnable() { - @Override - public void run() { - startAnimation(fadeOutAnimation); - if (getAnimation() != null) { - getAnimation().setAnimationListener(new Animation.AnimationListener() { - @Override - public void onAnimationStart(Animation animation) { - - } - - @Override - public void onAnimationEnd(Animation animation) { - if (isShown) { - position = position == texts.length - 1 ? 0 : position + 1; - startAnimation(); - } - } - - @Override - public void onAnimationRepeat(Animation animation) { - - } - }); - } - } - }, timeout); - } - } - - /** - * Stop the currently active animation - */ - private void stopAnimation() { - handler.removeCallbacksAndMessages(null); - if (getAnimation() != null) getAnimation().cancel(); - } - - - @IntDef({MILLISECONDS, SECONDS, MINUTES}) - @Retention(RetentionPolicy.SOURCE) - public @interface TimeUnit { - } -} diff --git a/fadingtextview/src/main/java/com/tomer/fadingtextview/FadingTextView.kt b/fadingtextview/src/main/java/com/tomer/fadingtextview/FadingTextView.kt new file mode 100644 index 0000000..a5a8c9e --- /dev/null +++ b/fadingtextview/src/main/java/com/tomer/fadingtextview/FadingTextView.kt @@ -0,0 +1,191 @@ +package com.tomer.fadingtextview + +import android.content.Context +import android.os.Handler +import android.os.Looper +import android.util.AttributeSet +import android.view.animation.Animation +import android.view.animation.AnimationUtils +import androidx.annotation.ArrayRes +import androidx.annotation.IntDef +import androidx.appcompat.widget.AppCompatTextView +import kotlin.math.abs + +class FadingTextView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : AppCompatTextView(context, attrs, defStyleAttr) { + + private val fadeInAnimation: Animation by lazy { AnimationUtils.loadAnimation(context, R.anim.fadein) } + private val fadeOutAnimation: Animation by lazy { AnimationUtils.loadAnimation(context, R.anim.fadeout) } + private val handler: Handler = Handler(Looper.getMainLooper()) + + var texts: Array = arrayOf() + private set + + private var isShown = false + private var position = 0 + private var timeout = DEFAULT_TIME_OUT + private var stopped = false + + init { + init() + handleAttrs(attrs) + } + + fun resume() { + isShown = true + startAnimation() + } + + fun pause() { + isShown = false + stopAnimation() + } + + fun stop() { + isShown = false + stopped = true + stopAnimation() + } + + fun restart() { + isShown = true + stopped = false + startAnimation() + invalidate() + } + + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + pause() + } + + override fun onAttachedToWindow() { + super.onAttachedToWindow() + resume() + } + + private fun init() { + isShown = true + } + + private fun handleAttrs(attrs: AttributeSet?) { + attrs?.let { attributeSet -> + val a = context.obtainStyledAttributes(attributeSet, R.styleable.FadingTextView) + a.getTextArray(R.styleable.FadingTextView_texts)?.let { textArray -> + texts = textArray + } + timeout = abs(a.getInteger(R.styleable.FadingTextView_timeout, 14500)) + + resources.getInteger(android.R.integer.config_longAnimTime) + val shuffle = a.getBoolean(R.styleable.FadingTextView_shuffle, false) + if (shuffle) { + shuffleTexts() + } + a.recycle() + } + } + + fun setTexts(texts: Array) { + require(texts.isNotEmpty()) { "There must be at least one text" } + this.texts = texts.map { it }.toTypedArray() + stopAnimation() + position = 0 + startAnimation() + } + + fun setTexts(@ArrayRes texts: Int) { + val mTexts = resources.getStringArray(texts) + require(mTexts.isNotEmpty()) { "There must be at least one text" } + this.texts = mTexts.map { it }.toTypedArray() + stopAnimation() + position = 0 + startAnimation() + } + + fun forceRefresh() { + stopAnimation() + startAnimation() + } + + fun fadeTo(position: Int) { + this.position = position + isShown = true + startAnimation() + pause() + } + + fun shuffleTexts() { + require(texts.isNotEmpty()) { "You must provide a string array to the FadingTextView using the texts parameter" } + val textsList = texts.toMutableList() + textsList.shuffle() + this.texts = textsList.toTypedArray() + } + + fun setTimeout(timeout: Int) { + require(timeout >= 1) { "Timeout must be longer than 0" } + this.timeout = timeout + } + + fun setTimeout(timeout: Double, @TimeUnit timeUnit: Int) { + require(!(timeout <= 0)) { "Timeout must be longer than 0" } + val multiplier: Int = when (timeUnit) { + MILLISECONDS -> 1 + SECONDS -> 1000 + MINUTES -> 60000 + else -> 1 + } + this.timeout = (timeout * multiplier).toInt() + } + + fun setTimeout(timeout: Long, timeUnit: java.util.concurrent.TimeUnit?) { + require(timeout > 0) { "Timeout must be longer than 0" } + this.timeout = java.util.concurrent.TimeUnit.MILLISECONDS + .convert(timeout, timeUnit).toInt() + } + + override fun startAnimation(animation: Animation) { + if (isShown && !stopped) { + super.startAnimation(animation) + } + } + + protected fun startAnimation() { + if (!isInEditMode) { + if (texts.isEmpty()) return + text = texts[position] + startAnimation(fadeInAnimation) + handler.postDelayed({ + startAnimation(fadeOutAnimation) + animation?.setAnimationListener(object : Animation.AnimationListener { + override fun onAnimationStart(animation: Animation) {} + override fun onAnimationEnd(animation: Animation) { + if (isShown) { + position = if (position == texts.size - 1) 0 else position + 1 + startAnimation() + } + } + + override fun onAnimationRepeat(animation: Animation) {} + }) + }, timeout.toLong()) + } + } + + private fun stopAnimation() { + handler.removeCallbacksAndMessages(null) + animation?.cancel() + } + + @Retention(AnnotationRetention.SOURCE) + @IntDef(MILLISECONDS, SECONDS, MINUTES) + annotation class TimeUnit + + companion object { + const val DEFAULT_TIME_OUT = 15000 + const val MILLISECONDS = 1 + const val SECONDS = 2 + const val MINUTES = 3 + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d2c45a4..3542d23 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Tue May 30 16:55:15 TRT 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists