From 15f9db5ca6e10c6fa59815635909b27b547ce66d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sat, 20 Mar 2021 20:11:50 +0100 Subject: [PATCH] [Gradle] Extract nachos dependency. --- app/build.gradle | 2 +- nachos/build.gradle | 23 - nachos/maven_push.gradle | 111 -- nachos/src/main/AndroidManifest.xml | 5 - .../hootsuite/nachos/ChipConfiguration.java | 78 -- .../com/hootsuite/nachos/NachoTextView.java | 1165 ----------------- .../java/com/hootsuite/nachos/chip/Chip.java | 30 - .../hootsuite/nachos/chip/ChipCreator.java | 44 - .../com/hootsuite/nachos/chip/ChipInfo.java | 20 - .../com/hootsuite/nachos/chip/ChipSpan.java | 511 -------- .../nachos/chip/ChipSpanChipCreator.java | 60 - .../terminator/ChipTerminatorHandler.java | 95 -- .../DefaultChipTerminatorHandler.java | 115 -- .../nachos/terminator/TextIterator.java | 63 - .../nachos/tokenizer/BaseChipTokenizer.java | 89 -- .../nachos/tokenizer/ChipTokenizer.java | 134 -- .../nachos/tokenizer/SpanChipTokenizer.java | 248 ---- .../validator/ChipifyingNachoValidator.java | 32 - .../validator/IllegalCharacterIdentifier.java | 5 - .../nachos/validator/NachoValidator.java | 30 - nachos/src/main/project.properties | 2 - .../res/color/chip_material_background.xml | 4 - nachos/src/main/res/values/attrs.xml | 12 - nachos/src/main/res/values/colors.xml | 5 - nachos/src/main/res/values/dimens.xml | 7 - nachos/src/main/res/values/strings.xml | 3 - nachos/src/main/res/values/styles.xml | 10 - settings.gradle | 2 +- 28 files changed, 2 insertions(+), 2903 deletions(-) delete mode 100644 nachos/build.gradle delete mode 100644 nachos/maven_push.gradle delete mode 100644 nachos/src/main/AndroidManifest.xml delete mode 100644 nachos/src/main/java/com/hootsuite/nachos/ChipConfiguration.java delete mode 100644 nachos/src/main/java/com/hootsuite/nachos/NachoTextView.java delete mode 100644 nachos/src/main/java/com/hootsuite/nachos/chip/Chip.java delete mode 100644 nachos/src/main/java/com/hootsuite/nachos/chip/ChipCreator.java delete mode 100644 nachos/src/main/java/com/hootsuite/nachos/chip/ChipInfo.java delete mode 100644 nachos/src/main/java/com/hootsuite/nachos/chip/ChipSpan.java delete mode 100644 nachos/src/main/java/com/hootsuite/nachos/chip/ChipSpanChipCreator.java delete mode 100644 nachos/src/main/java/com/hootsuite/nachos/terminator/ChipTerminatorHandler.java delete mode 100644 nachos/src/main/java/com/hootsuite/nachos/terminator/DefaultChipTerminatorHandler.java delete mode 100644 nachos/src/main/java/com/hootsuite/nachos/terminator/TextIterator.java delete mode 100644 nachos/src/main/java/com/hootsuite/nachos/tokenizer/BaseChipTokenizer.java delete mode 100644 nachos/src/main/java/com/hootsuite/nachos/tokenizer/ChipTokenizer.java delete mode 100644 nachos/src/main/java/com/hootsuite/nachos/tokenizer/SpanChipTokenizer.java delete mode 100644 nachos/src/main/java/com/hootsuite/nachos/validator/ChipifyingNachoValidator.java delete mode 100644 nachos/src/main/java/com/hootsuite/nachos/validator/IllegalCharacterIdentifier.java delete mode 100644 nachos/src/main/java/com/hootsuite/nachos/validator/NachoValidator.java delete mode 100644 nachos/src/main/project.properties delete mode 100644 nachos/src/main/res/color/chip_material_background.xml delete mode 100644 nachos/src/main/res/values/attrs.xml delete mode 100644 nachos/src/main/res/values/colors.xml delete mode 100644 nachos/src/main/res/values/dimens.xml delete mode 100644 nachos/src/main/res/values/strings.xml delete mode 100644 nachos/src/main/res/values/styles.xml diff --git a/app/build.gradle b/app/build.gradle index 56f5e4f4..2f2211d8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -171,7 +171,7 @@ dependencies { implementation "eu.szkolny:cafebar:5bf0c618de" implementation "eu.szkolny:material-about-library:0534abf316" implementation project(":mhttp") - implementation project(":nachos") + implementation "eu.szkolny:nachos:0e5dfcaceb" //implementation project(":Navigation") implementation project(":szkolny-font") diff --git a/nachos/build.gradle b/nachos/build.gradle deleted file mode 100644 index 821a4a83..00000000 --- a/nachos/build.gradle +++ /dev/null @@ -1,23 +0,0 @@ -apply plugin: 'com.android.library' - -android { - compileSdkVersion setup.compileSdk - - defaultConfig { - minSdkVersion 15 - targetSdkVersion setup.targetSdk - versionCode 1 - versionName "1.0" - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } -} - -dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation "com.android.support:support-compat:28.0.0" -} \ No newline at end of file diff --git a/nachos/maven_push.gradle b/nachos/maven_push.gradle deleted file mode 100644 index 946f7859..00000000 --- a/nachos/maven_push.gradle +++ /dev/null @@ -1,111 +0,0 @@ -apply plugin: 'maven' -apply plugin: 'signing' - -def isReleaseBuild() { - return VERSION_NAME.contains("SNAPSHOT") == false -} - -def getReleaseRepositoryUrl() { - return ARTIFACT_REPO -} - -def getSnapshotRepositoryUrl() { - return ARTIFACT_SNAPSHOT_REPO -} - -def getRepositoryUsername() { - return System.properties['username'] -} - -def getRepositoryPassword() { - return System.properties['password'] -} - -afterEvaluate { project -> - uploadArchives { - repositories { - mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - - pom.groupId = GROUP - pom.artifactId = POM_ARTIFACT_ID - pom.version = VERSION_NAME - - repository(url: getReleaseRepositoryUrl()) { - authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) - } - snapshotRepository(url: getSnapshotRepositoryUrl()) { - authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) - } - - pom.project { - name POM_NAME - packaging POM_PACKAGING - description POM_DESCRIPTION - url POM_URL - - scm { - url POM_SCM_URL - connection POM_SCM_CONNECTION - developerConnection POM_SCM_DEV_CONNECTION - } - - licenses { - license { - name POM_LICENCE_NAME - url POM_LICENCE_URL - distribution POM_LICENCE_DIST - } - } - - developers { - developer { - id POM_DEVELOPER_ID - name POM_DEVELOPER_NAME - } - } - } - } - } - } - - task installArchives(type: Upload) { - description "Installs the artifacts to the local Maven repository." - configuration = configurations['archives'] - repositories { - mavenDeployer { - pom.groupId = GROUP - pom.artifactId = POM_ARTIFACT_ID - pom.version = VERSION_NAME - - repository url: "file://${System.properties['user.home']}/.m2/repository" - } - } - } - - signing { - required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } - sign configurations.archives - } - - task androidJavadocs(type: Javadoc) { - source = android.sourceSets.main.java.srcDirs - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) - failOnError = false - } - - task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { - classifier = 'javadoc' - from androidJavadocs.destinationDir - } - - task androidSourcesJar(type: Jar) { - classifier = 'sources' - from android.sourceSets.main.java.srcDirs - } - - artifacts { - archives androidSourcesJar - archives androidJavadocsJar - } -} \ No newline at end of file diff --git a/nachos/src/main/AndroidManifest.xml b/nachos/src/main/AndroidManifest.xml deleted file mode 100644 index ad3cc65f..00000000 --- a/nachos/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/nachos/src/main/java/com/hootsuite/nachos/ChipConfiguration.java b/nachos/src/main/java/com/hootsuite/nachos/ChipConfiguration.java deleted file mode 100644 index 0b8fb3b7..00000000 --- a/nachos/src/main/java/com/hootsuite/nachos/ChipConfiguration.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.hootsuite.nachos; - -import android.content.res.ColorStateList; - -public class ChipConfiguration { - - private final int mChipHorizontalSpacing; - private final ColorStateList mChipBackground; - private final int mChipCornerRadius; - private final int mChipTextColor; - private final int mChipTextSize; - private final int mChipHeight; - private final int mChipVerticalSpacing; - private final int mMaxAvailableWidth; - - /** - * Creates a new ChipConfiguration. You can pass in {@code -1} or {@code null} for any of the parameters to indicate that parameter should be - * ignored. - * - * @param chipHorizontalSpacing the amount of horizontal space (in pixels) to put between consecutive chips - * @param chipBackground the {@link ColorStateList} to set as the background of the chips - * @param chipCornerRadius the corner radius of the chip background, in pixels - * @param chipTextColor the color to set as the text color of the chips - * @param chipTextSize the font size (in pixels) to use for the text of the chips - * @param chipHeight the height (in pixels) of each chip - * @param chipVerticalSpacing the amount of vertical space (in pixels) to put between chips on consecutive lines - * @param maxAvailableWidth the maximum available with for a chip (the width of a full line of text in the text view) - */ - ChipConfiguration(int chipHorizontalSpacing, - ColorStateList chipBackground, - int chipCornerRadius, - int chipTextColor, - int chipTextSize, - int chipHeight, - int chipVerticalSpacing, - int maxAvailableWidth) { - mChipHorizontalSpacing = chipHorizontalSpacing; - mChipBackground = chipBackground; - mChipCornerRadius = chipCornerRadius; - mChipTextColor = chipTextColor; - mChipTextSize = chipTextSize; - mChipHeight = chipHeight; - mChipVerticalSpacing = chipVerticalSpacing; - mMaxAvailableWidth = maxAvailableWidth; - } - - public int getChipHorizontalSpacing() { - return mChipHorizontalSpacing; - } - - public ColorStateList getChipBackground() { - return mChipBackground; - } - - public int getChipCornerRadius() { - return mChipCornerRadius; - } - - public int getChipTextColor() { - return mChipTextColor; - } - - public int getChipTextSize() { - return mChipTextSize; - } - - public int getChipHeight() { - return mChipHeight; - } - - public int getChipVerticalSpacing() { - return mChipVerticalSpacing; - } - - public int getMaxAvailableWidth() { - return mMaxAvailableWidth; - } -} diff --git a/nachos/src/main/java/com/hootsuite/nachos/NachoTextView.java b/nachos/src/main/java/com/hootsuite/nachos/NachoTextView.java deleted file mode 100644 index b2cb81ab..00000000 --- a/nachos/src/main/java/com/hootsuite/nachos/NachoTextView.java +++ /dev/null @@ -1,1165 +0,0 @@ -package com.hootsuite.nachos; - -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -import android.content.res.ColorStateList; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.graphics.Paint; -import android.text.Editable; -import android.text.Layout; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.util.AttributeSet; -import android.util.Log; -import android.util.Pair; -import android.view.GestureDetector; -import android.view.MotionEvent; -import android.view.View; -import android.view.inputmethod.EditorInfo; -import android.widget.Adapter; -import android.widget.AdapterView; -import android.widget.AutoCompleteTextView; -import android.widget.ListAdapter; -import android.widget.MultiAutoCompleteTextView; - -import androidx.annotation.ColorInt; -import androidx.annotation.ColorRes; -import androidx.annotation.DimenRes; -import androidx.annotation.Dimension; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; - -import com.hootsuite.nachos.chip.Chip; -import com.hootsuite.nachos.chip.ChipInfo; -import com.hootsuite.nachos.chip.ChipSpan; -import com.hootsuite.nachos.chip.ChipSpanChipCreator; -import com.hootsuite.nachos.terminator.ChipTerminatorHandler; -import com.hootsuite.nachos.terminator.DefaultChipTerminatorHandler; -import com.hootsuite.nachos.tokenizer.ChipTokenizer; -import com.hootsuite.nachos.tokenizer.SpanChipTokenizer; -import com.hootsuite.nachos.validator.ChipifyingNachoValidator; -import com.hootsuite.nachos.validator.IllegalCharacterIdentifier; -import com.hootsuite.nachos.validator.NachoValidator; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -/** - * An editable TextView extending {@link MultiAutoCompleteTextView} that supports "chipifying" pieces of text and displaying suggestions for segments of the text. - *

The ChipTokenizer

- * To customize chipifying with this class you can provide a custom {@link ChipTokenizer} by calling {@link #setChipTokenizer(ChipTokenizer)}. - * By default the {@link SpanChipTokenizer} is used. - *

Chip Terminators

- * To set which characters trigger the creation of a chip, call {@link #addChipTerminator(char, int)} or {@link #setChipTerminators(Map)}. - * For example if tapping enter should cause all unchipped text to become chipped, call - * {@code chipSuggestionTextView.addChipTerminator('\n', ChipTerminatorHandler.BEHAVIOR_CHIPIFY_ALL);} - * To completely customize how chips are created when text is entered in this text view you can provide a custom {@link ChipTerminatorHandler} - * through {@link #setChipTerminatorHandler(ChipTerminatorHandler)} - *

Illegal Characters

- * To prevent a character from being typed you can call {@link #setIllegalCharacterIdentifier(IllegalCharacterIdentifier)}} to identify characters - * that should be considered illegal. - *

Suggestions

- * To provide suggestions you must provide an {@link android.widget.Adapter} by calling {@link #setAdapter(ListAdapter)} - *

UI Customization

- * This view defines six custom attributes (all of which are optional): - * - * The values of these attributes will be passed to the ChipTokenizer through {@link ChipTokenizer#applyConfiguration(Editable, ChipConfiguration)} - *

Validation

- * This class can perform validation when certain events occur (such as losing focus). When the validation occurs is decided by - * {@link AutoCompleteTextView}. To perform validation, set a {@link NachoValidator}: - *
- *         nachoTextView.setNachoValidator(new ChipifyingNachoValidator());
- *     
- * Note: The NachoValidator will be ignored if a ChipTokenizer is not set. To perform validation without a ChipTokenizer you can use - * {@link AutoCompleteTextView}'s built-in {@link AutoCompleteTextView.Validator Validator} through {@link #setValidator(Validator)} - *

Editing Chips

- * This class also supports editing chips on touch. To enable this behavior call {@link #enableEditChipOnTouch(boolean, boolean)}. To disable this - * behavior you can call {@link #disableEditChipOnTouch()} - *

Example Setup:

- * A standard setup for this class could look something like the following: - *
- *         String[] suggestions = new String[]{"suggestion 1", "suggestion 2"};
- *         ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, suggestions);
- *         nachoTextView.setAdapter(adapter);
- *         nachoTextView.addChipTerminator('\n', ChipTerminatorHandler.BEHAVIOR_CHIPIFY_ALL);
- *         nachoTextView.addChipTerminator(' ', ChipTerminatorHandler.BEHAVIOR_CHIPIFY_TO_TERMINATOR);
- *         nachoTextView.setIllegalCharacters('@');
- *         nachoTextView.setNachoValidator(new ChipifyingNachoValidator());
- *         nachoTextView.enableEditChipOnTouch(true, true);
- *         nachoTextView.setOnChipClickListener(new NachoTextView.OnChipClickListener() {
- *            {@literal @Override}
- *             public void onChipClick(Chip chip, MotionEvent motionEvent) {
- *                 // Handle click event
- *             }
- *         });
- *         nachoTextView.setOnChipRemoveListener(new NachoTextView.OnChipRemoveListener() {
- *            {@literal @Override}
- *             public void onChipRemove(Chip chip) {
- *                 // Handle remove event
- *             }
- *         });
- *     
- * - * @see SpanChipTokenizer - * @see DefaultChipTerminatorHandler - * @see ChipifyingNachoValidator - */ -public class NachoTextView extends MultiAutoCompleteTextView implements TextWatcher, AdapterView.OnItemClickListener { - - // UI Attributes - private int mChipHorizontalSpacing = -1; - private ColorStateList mChipBackground = null; - private int mChipCornerRadius = -1; - private int mChipTextColor = Color.TRANSPARENT; - private int mChipTextSize = -1; - private int mChipHeight = -1; - private int mChipVerticalSpacing = -1; - - private int mDefaultPaddingTop = 0; - private int mDefaultPaddingBottom = 0; - /** - * Flag to keep track of the padding state so we only update the padding when necessary - */ - private boolean mUsingDefaultPadding = true; - - // Touch events - @Nullable - private OnChipClickListener mOnChipClickListener; - private GestureDetector singleTapDetector; - private boolean mEditChipOnTouchEnabled; - private boolean mMoveChipToEndOnEdit; - private boolean mChipifyUnterminatedTokensOnEdit; - - // Text entry - @Nullable - private ChipTokenizer mChipTokenizer; - @Nullable - private ChipTerminatorHandler mChipTerminatorHandler; - @Nullable - private NachoValidator mNachoValidator; - @Nullable - private IllegalCharacterIdentifier illegalCharacterIdentifier; - - @Nullable - private OnChipRemoveListener mOnChipRemoveListener; - private List mChipsToRemove = new ArrayList<>(); - private boolean mIgnoreTextChangedEvents; - private int mTextChangedStart; - private int mTextChangedEnd; - private boolean mIsPasteEvent; - - // Measurement - private boolean mMeasured; - - // Layout - private boolean mLayoutComplete; - - public NachoTextView(Context context) { - super(context); - init(null); - } - - public NachoTextView(Context context, AttributeSet attrs) { - super(context, attrs); - init(attrs); - } - - public NachoTextView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(attrs); - } - - private void init(@Nullable AttributeSet attrs) { - Context context = getContext(); - - if (attrs != null) { - TypedArray attributes = context.getTheme().obtainStyledAttributes( - attrs, - R.styleable.NachoTextView, - 0, - R.style.DefaultChipSuggestionTextView); - - try { - mChipHorizontalSpacing = attributes.getDimensionPixelSize(R.styleable.NachoTextView_chipHorizontalSpacing, -1); - mChipBackground = attributes.getColorStateList(R.styleable.NachoTextView_chipBackground); - mChipCornerRadius = attributes.getDimensionPixelSize(R.styleable.NachoTextView_chipCornerRadius, -1); - mChipTextColor = attributes.getColor(R.styleable.NachoTextView_chipTextColor, Color.TRANSPARENT); - mChipTextSize = attributes.getDimensionPixelSize(R.styleable.NachoTextView_chipTextSize, -1); - mChipHeight = attributes.getDimensionPixelSize(R.styleable.NachoTextView_chipHeight, -1); - mChipVerticalSpacing = attributes.getDimensionPixelSize(R.styleable.NachoTextView_chipVerticalSpacing, -1); - } finally { - attributes.recycle(); - } - } - - mDefaultPaddingTop = getPaddingTop(); - mDefaultPaddingBottom = getPaddingBottom(); - - singleTapDetector = new GestureDetector(getContext(), new SingleTapListener()); - - setImeOptions(EditorInfo.IME_FLAG_NO_FULLSCREEN); - addTextChangedListener(this); - setChipTokenizer(new SpanChipTokenizer<>(context, new ChipSpanChipCreator(), ChipSpan.class)); - setChipTerminatorHandler(new DefaultChipTerminatorHandler()); - setOnItemClickListener(this); - - updatePadding(); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - if (!mMeasured && getWidth() > 0) { - // Refresh the tokenizer for width changes - invalidateChips(); - mMeasured = true; - } - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - - if (!mLayoutComplete) { - invalidateChips(); - mLayoutComplete = true; - } - } - - /** - * Updates the padding based on whether or not any chips are present to avoid the view from changing heights when chips are inserted/deleted. - * Extra padding is added when there are no chips. When there are chips the padding is reverted to its defaults. This only affects top and bottom - * padding because the chips only affect the height of the view. - */ - private void updatePadding() { - if (mChipHeight != -1) { - boolean chipsArePresent = !getAllChips().isEmpty(); - if (!chipsArePresent && mUsingDefaultPadding) { - mUsingDefaultPadding = false; - Paint paint = getPaint(); - Paint.FontMetricsInt fm = paint.getFontMetricsInt(); - int textHeight = fm.descent - fm.ascent; - // Calculate how tall the view should be if there were chips - int newTextHeight = mChipHeight + (mChipVerticalSpacing != -1 ? mChipVerticalSpacing : 0); - // We need to add half our missing height above and below the text by increasing top and bottom padding - int paddingAdjustment = (newTextHeight - textHeight) / 2; - super.setPadding(getPaddingLeft(), mDefaultPaddingTop + paddingAdjustment, getPaddingRight(), mDefaultPaddingBottom + paddingAdjustment); - } else if (chipsArePresent && !mUsingDefaultPadding) { - // If there are chips we can revert to default padding - mUsingDefaultPadding = true; - super.setPadding(getPaddingLeft(), mDefaultPaddingTop, getPaddingRight(), mDefaultPaddingBottom); - } - } - } - - /** - * Sets the padding on this View. The left and right padding will be handled as they normally would in a TextView. The top and bottom padding passed - * here will be the padding that is used when there are one or more chips in the text view. When there are no chips present, the padding will be - * increased to make sure the overall height of the text view stays the same, since chips take up more vertical space than plain text. - * - * @param left the left padding in pixels - * @param top the top padding in pixels - * @param right the right padding in pixels - * @param bottom the bottom padding in pixels - */ - @Override - public void setPadding(int left, int top, int right, int bottom) { - // Call the super method so that left and right padding are updated - // top and bottom padding will be handled in updatePadding() - super.setPadding(left, top, right, bottom); - mDefaultPaddingTop = top; - mDefaultPaddingBottom = bottom; - updatePadding(); - } - - public int getChipHorizontalSpacing() { - return mChipHorizontalSpacing; - } - - public void setChipHorizontalSpacing(@DimenRes int chipHorizontalSpacingResId) { - mChipHorizontalSpacing = getContext().getResources().getDimensionPixelSize(chipHorizontalSpacingResId); - invalidateChips(); - } - - public ColorStateList getChipBackground() { - return mChipBackground; - } - - public void setChipBackgroundResource(@ColorRes int chipBackgroundResId) { - setChipBackground(ContextCompat.getColorStateList(getContext(), chipBackgroundResId)); - } - - public void setChipBackground(ColorStateList chipBackground) { - mChipBackground = chipBackground; - invalidateChips(); - } - - /** - * @return The chip background corner radius value, in pixels. - */ - @Dimension - public int getChipCornerRadius() { - return mChipCornerRadius; - } - - /** - * Sets the chip background corner radius. - * - * @param chipCornerRadiusResId The dimension resource with the corner radius value. - */ - public void setChipCornerRadiusResource(@DimenRes int chipCornerRadiusResId) { - setChipCornerRadius(getContext().getResources().getDimensionPixelSize(chipCornerRadiusResId)); - } - - /** - * Sets the chip background corner radius. - * - * @param chipCornerRadius The corner radius value, in pixels. - */ - public void setChipCornerRadius(@Dimension int chipCornerRadius) { - mChipCornerRadius = chipCornerRadius; - invalidateChips(); - } - - - public int getChipTextColor() { - return mChipTextColor; - } - - public void setChipTextColorResource(@ColorRes int chipTextColorResId) { - setChipTextColor(ContextCompat.getColor(getContext(), chipTextColorResId)); - } - - public void setChipTextColor(@ColorInt int chipTextColor) { - mChipTextColor = chipTextColor; - invalidateChips(); - } - - public int getChipTextSize() { - return mChipTextSize; - } - - public void setChipTextSize(@DimenRes int chipTextSizeResId) { - mChipTextSize = getContext().getResources().getDimensionPixelSize(chipTextSizeResId); - invalidateChips(); - } - - public int getChipHeight() { - return mChipHeight; - } - - public void setChipHeight(@DimenRes int chipHeightResId) { - mChipHeight = getContext().getResources().getDimensionPixelSize(chipHeightResId); - invalidateChips(); - } - - public int getChipVerticalSpacing() { - return mChipVerticalSpacing; - } - - public void setChipVerticalSpacing(@DimenRes int chipVerticalSpacingResId) { - mChipVerticalSpacing = getContext().getResources().getDimensionPixelSize(chipVerticalSpacingResId); - invalidateChips(); - } - - @Nullable - public ChipTokenizer getChipTokenizer() { - return mChipTokenizer; - } - - /** - * Sets the {@link ChipTokenizer} to be used by this ChipSuggestionTextView. - * Note that a Tokenizer set here will override any Tokenizer set by {@link #setTokenizer(Tokenizer)} - * - * @param chipTokenizer the {@link ChipTokenizer} to set - */ - public void setChipTokenizer(@Nullable ChipTokenizer chipTokenizer) { - mChipTokenizer = chipTokenizer; - if (mChipTokenizer != null) { - setTokenizer(new ChipTokenizerWrapper(mChipTokenizer)); - } else { - setTokenizer(null); - } - invalidateChips(); - } - - public void setOnChipClickListener(@Nullable OnChipClickListener onChipClickListener) { - mOnChipClickListener = onChipClickListener; - } - - public void setOnChipRemoveListener(@Nullable OnChipRemoveListener onChipRemoveListener) { - mOnChipRemoveListener = onChipRemoveListener; - } - - public void setChipTerminatorHandler(@Nullable ChipTerminatorHandler chipTerminatorHandler) { - mChipTerminatorHandler = chipTerminatorHandler; - } - - public void setNachoValidator(@Nullable NachoValidator nachoValidator) { - mNachoValidator = nachoValidator; - } - - /** - * @see ChipTerminatorHandler#setChipTerminators(Map) - */ - public void setChipTerminators(@Nullable Map chipTerminators) { - if (mChipTerminatorHandler != null) { - mChipTerminatorHandler.setChipTerminators(chipTerminators); - } - } - - /** - * @see ChipTerminatorHandler#addChipTerminator(char, int) - */ - public void addChipTerminator(char character, int behavior) { - if (mChipTerminatorHandler != null) { - mChipTerminatorHandler.addChipTerminator(character, behavior); - } - } - - /** - * @see ChipTerminatorHandler#setPasteBehavior(int) - */ - public void setPasteBehavior(int pasteBehavior) { - if (mChipTerminatorHandler != null) { - mChipTerminatorHandler.setPasteBehavior(pasteBehavior); - } - } - - /** - * Sets the {@link IllegalCharacterIdentifier} that will identify characters that should - * not show up in the field when typed (i.e. they will be deleted as soon as they are entered). - * If a character is listed as both a chip terminator character and an illegal character, - * it will be treated as an illegal character. - * - * @param illegalCharacterIdentifier the identifier to use - */ - public void setIllegalCharacterIdentifier(@Nullable IllegalCharacterIdentifier illegalCharacterIdentifier) { - this.illegalCharacterIdentifier = illegalCharacterIdentifier; - } - - /** - * Applies any updated configuration parameters to any existing chips and all future chips in the text view. - * - * @see ChipTokenizer#applyConfiguration(Editable, ChipConfiguration) - */ - public void invalidateChips() { - beginUnwatchedTextChange(); - - if (mChipTokenizer != null) { - Editable text = getText(); - int availableWidth = getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight(); - ChipConfiguration configuration = new ChipConfiguration( - mChipHorizontalSpacing, - mChipBackground, - mChipCornerRadius, - mChipTextColor, - mChipTextSize, - mChipHeight, - mChipVerticalSpacing, - availableWidth); - - mChipTokenizer.applyConfiguration(text, configuration); - } - - endUnwatchedTextChange(); - } - - /** - * Enables editing chips on touch events. When a touch event occurs, the touched chip will be put in editing mode. To later disable this behavior - * call {@link #disableEditChipOnTouch()}. - *

- * Note: If an {@link OnChipClickListener} is set it's behavior will override the behavior described here if it's - * {@link OnChipClickListener#onChipClick(Chip, MotionEvent)} method returns true. If that method returns false, the touched chip will be put - * in editing mode as expected. - *

- * - * @param moveChipToEnd if true, the chip will also be moved to the end of the text when it is put in editing mode - * @param chipifyUnterminatedTokens if true, all unterminated tokens will be chipified before the touched chip is put in editing mode - * @see #disableEditChipOnTouch() - */ - public void enableEditChipOnTouch(boolean moveChipToEnd, boolean chipifyUnterminatedTokens) { - mEditChipOnTouchEnabled = true; - mMoveChipToEndOnEdit = moveChipToEnd; - mChipifyUnterminatedTokensOnEdit = chipifyUnterminatedTokens; - } - - /** - * Disables editing chips on touch events. To re-enable this behavior call {@link #enableEditChipOnTouch(boolean, boolean)}. - * - * @see #enableEditChipOnTouch(boolean, boolean) - */ - public void disableEditChipOnTouch() { - mEditChipOnTouchEnabled = false; - } - - /** - * Puts the provided Chip in editing mode (i.e. reverts it to an unchipified token whose text can be edited). - * - * @param chip the chip to edit - * @param moveChipToEnd if true, the chip will also be moved to the end of the text - */ - public void setEditingChip(Chip chip, boolean moveChipToEnd) { - if (mChipTokenizer == null) { - return; - } - - beginUnwatchedTextChange(); - - Editable text = getText(); - if (moveChipToEnd) { - // Move the chip text to the end of the text - text.append(chip.getText()); - // Delete the existing chip - mChipTokenizer.deleteChipAndPadding(chip, text); - // Move the cursor to the end of the text - setSelection(text.length()); - } else { - int chipStart = mChipTokenizer.findChipStart(chip, text); - mChipTokenizer.revertChipToToken(chip, text); - setSelection(mChipTokenizer.findTokenEnd(text, chipStart)); - } - - endUnwatchedTextChange(); - } - - @Override - public boolean onTouchEvent(@NonNull MotionEvent event) { - boolean wasHandled = false; - clearChipStates(); - Chip touchedChip = findTouchedChip(event); - if (touchedChip != null && isFocused() && singleTapDetector.onTouchEvent(event)) { - touchedChip.setState(View.PRESSED_SELECTED_STATE_SET); - if (onChipClicked(touchedChip)) { - wasHandled = true; - } - if (mOnChipClickListener != null) { - mOnChipClickListener.onChipClick(touchedChip, event); - } - } - - // Getting NullPointerException inside Editor.updateFloatingToolbarVisibility (Editor.java:1520) - // primarily seen in Samsung Nougat devices. - boolean superOnTouch = false; - try { - superOnTouch = super.onTouchEvent(event); - } catch (NullPointerException e) { - Log.w("Nacho", String.format("Error during touch event of type [%d]", event.getAction()), e); - // can't handle or reproduce, but will monitor the error - } - - return wasHandled || superOnTouch; - } - - @Nullable - private Chip findTouchedChip(MotionEvent event) { - if (mChipTokenizer == null) { - return null; - } - - Editable text = getText(); - int offset = getOffsetForPosition(event.getX(), event.getY()); - List chips = getAllChips(); - for (Chip chip : chips) { - int chipStart = mChipTokenizer.findChipStart(chip, text); - int chipEnd = mChipTokenizer.findChipEnd(chip, text); // This is actually the index of the character just past the end of the chip - // When a touch event occurs getOffsetForPosition will either return the index of the first character of the span or the index of the - // character one past the end of the span - // This matches up perfectly with chipStart and chipEnd so we can just directly compare them... - if (chipStart <= offset && offset <= chipEnd) { - float startX = getXForIndex(chipStart); - float endX = getXForIndex(chipEnd - 1); - float eventX = event.getX(); - // ... however, when comparing the x coordinate we need to use (chipEnd - 1) because chipEnd will give us the x coordinate of the - // beginning of the next span since that is actually what chipEnd holds. We want the x coordinate of the end of the current span so - // we use (chipEnd - 1) - if (startX <= eventX && eventX <= endX) { - return chip; - } - } - } - return null; - } - - /** - * Implement this method to handle chip clicked events. - * - * @param chip the chip that was clicked - * @return true if the event was handled, otherwise false - */ - public boolean onChipClicked(Chip chip) { - boolean wasHandled = false; - if (mEditChipOnTouchEnabled) { - if (mChipifyUnterminatedTokensOnEdit) { - chipifyAllUnterminatedTokens(); - } - setEditingChip(chip, mMoveChipToEndOnEdit); - wasHandled = true; - } - return wasHandled; - } - - private float getXForIndex(int index) { - Layout layout = getLayout(); - return layout.getPrimaryHorizontal(index); - } - - private void clearChipStates() { - for (Chip chip : getAllChips()) { - chip.setState(View.EMPTY_STATE_SET); - } - } - - @Override - public boolean onTextContextMenuItem(int id) { - int start = getSelectionStart(); - int end = getSelectionEnd(); - switch (id) { - case android.R.id.cut: - try { - setClipboardData(ClipData.newPlainText(null, getTextWithPlainTextSpans(start, end))); - } catch (StringIndexOutOfBoundsException e) { - throw new StringIndexOutOfBoundsException( - String.format( - "%s \nError cutting text index [%s, %s] for text [%s] and substring [%s]", - e.getMessage(), - start, - end, - getText().toString(), - getText().subSequence(start, end))); - } - getText().delete(getSelectionStart(), getSelectionEnd()); - return true; - case android.R.id.copy: - try { - setClipboardData(ClipData.newPlainText(null, getTextWithPlainTextSpans(start, end))); - } catch (StringIndexOutOfBoundsException e) { - throw new StringIndexOutOfBoundsException( - String.format( - "%s \nError copying text index [%s, %s] for text [%s] and substring [%s]", - e.getMessage(), - start, - end, - getText().toString(), - getText().subSequence(start, end))); - } - return true; - case android.R.id.paste: - mIsPasteEvent = true; - boolean returnValue = super.onTextContextMenuItem(id); - mIsPasteEvent = false; - return returnValue; - default: - return super.onTextContextMenuItem(id); - } - } - - private void setClipboardData(ClipData clip) { - ClipboardManager clipboard = (ClipboardManager) getContext(). - getSystemService(Context.CLIPBOARD_SERVICE); - clipboard.setPrimaryClip(clip); - } - - /** - * If a {@link android.widget.AutoCompleteTextView.Validator Validator} was set, this method will validate the entire text. - * (Overrides the superclass method which only validates the current token) - */ - @Override - public void performValidation() { - if (mNachoValidator == null || mChipTokenizer == null) { - super.performValidation(); - return; - } - - CharSequence text = getText(); - if (!TextUtils.isEmpty(text) && !mNachoValidator.isValid(mChipTokenizer, text)) { - setRawText(mNachoValidator.fixText(mChipTokenizer, text)); - } - } - - /** - * From the point this method is called to when {@link #endUnwatchedTextChange()} is called, all TextChanged events will be ignored - */ - private void beginUnwatchedTextChange() { - mIgnoreTextChangedEvents = true; - } - - /** - * After this method is called TextChanged events will resume being handled. - * This method also calls {@link #updatePadding()} in case the unwatched changed created/destroyed chips - */ - private void endUnwatchedTextChange() { - updatePadding(); - mIgnoreTextChangedEvents = false; - } - - /** - * Sets the contents of this text view without performing any processing (nothing will be chipified, no characters will be removed etc.) - * - * @param text the text to set - */ - private void setRawText(CharSequence text) { - beginUnwatchedTextChange(); - super.setText(text); - endUnwatchedTextChange(); - } - - /** - * Sets the contents of this text view to contain the provided list of strings. The text view will be cleared then each string in the list will - * be chipified and appended to the text. - * - * @param chipValues the list of strings to chipify and set as the contents of the text view or null to clear the text view - */ - public void setText(@Nullable List chipValues) { - if (mChipTokenizer == null) { - return; - } - beginUnwatchedTextChange(); - - Editable text = getText(); - text.clear(); - - if (chipValues != null) { - for (String chipValue : chipValues) { - CharSequence chippedText = mChipTokenizer.terminateToken(chipValue, null); - text.append(chippedText); - } - } - setSelection(text.length()); - - endUnwatchedTextChange(); - } - - public void setTextWithChips(@Nullable List chips) { - if (mChipTokenizer == null) { - return; - } - beginUnwatchedTextChange(); - - Editable text = getText(); - text.clear(); - - if (chips != null) { - for (ChipInfo chipInfo : chips) { - CharSequence chippedText = mChipTokenizer.terminateToken(chipInfo.getText(), chipInfo.getData()); - text.append(chippedText); - } - } - setSelection(text.length()); - endUnwatchedTextChange(); - } - - public void addTextWithChips(@Nullable List chips) { - if (mChipTokenizer == null) { - return; - } - beginUnwatchedTextChange(); - - Editable text = getText(); - - if (chips != null) { - for (ChipInfo chipInfo : chips) { - CharSequence chippedText = mChipTokenizer.terminateToken(chipInfo.getText(), chipInfo.getData()); - text.append(chippedText); - } - } - setSelection(text.length()); - endUnwatchedTextChange(); - } - - @Override - public void onItemClick(AdapterView adapterView, View view, int position, long id) { - if (mChipTokenizer == null) { - return; - } - Adapter adapter = getAdapter(); - if (adapter == null) { - return; - } - beginUnwatchedTextChange(); - - Object data = getDataForSuggestion(adapter, position); - CharSequence text = getFilter().convertResultToString(adapter.getItem(position)); - - clearComposingText(); - int end = getSelectionEnd(); - Editable editable = getText(); - int start = mChipTokenizer.findTokenStart(editable, end); - - // guard against java.lang.StringIndexOutOfBoundsException - start = Math.min(Math.max(0, start), editable.length()); - end = Math.min(Math.max(0, end), editable.length()); - if (end < start) { - end = start; - } - - editable.replace(start, end, mChipTokenizer.terminateToken(text, data)); - - endUnwatchedTextChange(); - } - - /** - * Returns a object that will be associated with a chip that is about to be created for the item at {@code position} in {@code adapter} because that - * item was just tapped. - * - * @param adapter the adapter supplying the suggestions - * @param position the position of the suggestion that was tapped - * @return the data object - */ - protected Object getDataForSuggestion(@NonNull Adapter adapter, int position) { - return adapter.getItem(position); - } - - /** - * If there is a ChipTokenizer set, this method will do nothing. Instead we wait until the OnItemClickListener is triggered to actually perform - * the text replacement so we can also associate the suggestion data with it. - *

- * If there is no ChipTokenizer set, we call through to the super method. - * - * @param text the text to be chipified - */ - @Override - protected void replaceText(CharSequence text) { - // If we have a ChipTokenizer, this will be handled by our OnItemClickListener so we can do nothing here. - // If we don't have a ChipTokenizer, we'll use the default behavior - if (mChipTokenizer == null) { - super.replaceText(text); - } - } - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - if (mIgnoreTextChangedEvents) { - return; - } - - mTextChangedStart = start; - mTextChangedEnd = start + after; - - // Check for backspace - if (mChipTokenizer != null) { - if (count > 0 && after < count) { - int end = start + count; - Editable message = getText(); - Chip[] chips = mChipTokenizer.findAllChips(start, end, message); - - for (Chip chip : chips) { - int spanStart = mChipTokenizer.findChipStart(chip, message); - int spanEnd = mChipTokenizer.findChipEnd(chip, message); - if ((spanStart < end) && (spanEnd > start)) { - // Add to remove list - mChipsToRemove.add(chip); - } - } - } - } - } - - @Override - public void onTextChanged(@NonNull CharSequence textChanged, int start, int before, int count) { - } - - @Override - public void afterTextChanged(Editable message) { - if (mIgnoreTextChangedEvents) { - return; - } - - // Avoid triggering text changed events from changes we make in this method - beginUnwatchedTextChange(); - - // Handle backspace key - if (mChipTokenizer != null) { - Iterator iterator = mChipsToRemove.iterator(); - while (iterator.hasNext()) { - Chip chip = iterator.next(); - iterator.remove(); - mChipTokenizer.deleteChip(chip, message); - if (mOnChipRemoveListener != null) { - mOnChipRemoveListener.onChipRemove(chip); - } - } - } - - // Handle an illegal or chip terminator character - if (message.length() >= mTextChangedEnd && message.length() >= mTextChangedStart) { - handleTextChanged(mTextChangedStart, mTextChangedEnd); - } - - endUnwatchedTextChange(); - } - - private void handleTextChanged(int start, int end) { - if (start == end) { - // If start and end are the same there was text deleted, so this type of event can be ignored - return; - } - - // First remove any illegal characters - Editable text = getText(); - CharSequence subText = text.subSequence(start, end); - CharSequence withoutIllegalCharacters = removeIllegalCharacters(subText); - - // Check if illegal characters were found - if (withoutIllegalCharacters.length() < subText.length()) { - text.replace(start, end, withoutIllegalCharacters); - end = start + withoutIllegalCharacters.length(); - clearComposingText(); - } - - if (start == end) { - // If start and end are the same here, it means only illegal characters were inserted so there's nothing left to do - return; - } - - // Then handle chip terminator characters - if (mChipTokenizer != null && mChipTerminatorHandler != null) { - int newSelectionIndex = mChipTerminatorHandler.findAndHandleChipTerminators(mChipTokenizer, getText(), start, end, mIsPasteEvent); - if (newSelectionIndex > 0) { - setSelection(newSelectionIndex); - } - } - } - - private CharSequence removeIllegalCharacters(CharSequence text) { - StringBuilder newText = new StringBuilder(); - - for (int i = 0; i < text.length(); i++) { - char theChar = text.charAt(i); - if (!isIllegalCharacter(theChar)) { - newText.append(theChar); - } - } - - return newText; - } - - private boolean isIllegalCharacter(char character) { - if (illegalCharacterIdentifier != null) { - return illegalCharacterIdentifier.isCharacterIllegal(character); - } - return false; - } - - /** - * Chipifies all existing plain text in the field - */ - public void chipifyAllUnterminatedTokens() { - beginUnwatchedTextChange(); - chipifyAllUnterminatedTokens(getText()); - endUnwatchedTextChange(); - } - - private void chipifyAllUnterminatedTokens(Editable text) { - if (mChipTokenizer != null) { - mChipTokenizer.terminateAllTokens(text); - } - } - - /** - * Replaces the text from start (inclusive) to end (exclusive) with a chip - * containing the same text - * - * @param start the index of the first character to replace - * @param end one more than the index of the last character to replace - */ - public void chipify(int start, int end) { - beginUnwatchedTextChange(); - chipify(start, end, getText(), null); - endUnwatchedTextChange(); - } - - private void chipify(int start, int end, Editable text, Object data) { - if (mChipTokenizer != null) { - CharSequence textToChip = text.subSequence(start, end); - CharSequence chippedText = mChipTokenizer.terminateToken(textToChip, data); - text.replace(start, end, chippedText); - } - } - - private CharSequence getTextWithPlainTextSpans(int start, int end) { - Editable editable = getText(); - String selectedText = editable.subSequence(start, end).toString(); - - if (mChipTokenizer != null) { - List chips = Arrays.asList(mChipTokenizer.findAllChips(start, end, editable)); - Collections.reverse(chips); - for (Chip chip : chips) { - String chipText = chip.getText().toString(); - int chipStart = mChipTokenizer.findChipStart(chip, editable) - start; - int chipEnd = mChipTokenizer.findChipEnd(chip, editable) - start; - selectedText = selectedText.substring(0, chipStart) + chipText + selectedText.substring(chipEnd, selectedText.length()); - } - } - return selectedText; - } - - /** - * @return all of the chips currently in the text view - this does not include any unchipped text - */ - @NonNull - public List getAllChips() { - Editable text = getText(); - return mChipTokenizer != null ? Arrays.asList(mChipTokenizer.findAllChips(0, text.length(), text)) : new ArrayList(); - } - - /** - * Returns a List of the string values of all the chips in the text (obtained through {@link Chip#getText()}). - * This does not include the text of any unterminated tokens. - * - * @return the List of chip values - */ - @NonNull - public List getChipValues() { - List chipValues = new ArrayList<>(); - - List chips = getAllChips(); - for (Chip chip : chips) { - chipValues.add(chip.getText().toString()); - } - - return chipValues; - } - - /** - * Returns a List of the string values of all the tokens (unchipped text) in the text - * (obtained through {@link ChipTokenizer#findAllTokens(CharSequence)}). This does not include any chipped text. - * - * @return the List of token values - */ - @NonNull - public List getTokenValues() { - List tokenValues = new ArrayList<>(); - - if (mChipTokenizer != null) { - Editable text = getText(); - List> unterminatedTokenIndexes = mChipTokenizer.findAllTokens(text); - for (Pair indexes : unterminatedTokenIndexes) { - String tokenValue = text.subSequence(indexes.first, indexes.second).toString(); - tokenValues.add(tokenValue); - } - } - - return tokenValues; - } - - /** - * Returns a combination of the chip values and token values in the text. - * - * @return the List of all chip and token values - * @see #getChipValues() - * @see #getTokenValues() - */ - @NonNull - public List getChipAndTokenValues() { - List chipAndTokenValues = new ArrayList<>(); - chipAndTokenValues.addAll(getChipValues()); - chipAndTokenValues.addAll(getTokenValues()); - return chipAndTokenValues; - } - - public boolean ignoreThreshold = false; - - @Override - public boolean enoughToFilter() { - return ignoreThreshold || super.enoughToFilter(); - } - - public OnDismissListener onDismissListener = null; - - @Override - public void dismissDropDown() { - if (onDismissListener != null) - onDismissListener.onDismiss(); - super.dismissDropDown(); - } - - @Override - public String toString() { - try { - return getTextWithPlainTextSpans(0, getText().length()).toString(); - } catch (ClassCastException ex) { // Exception is thrown by cast in getText() on some LG devices - return super.toString(); - } catch (StringIndexOutOfBoundsException e) { - throw new StringIndexOutOfBoundsException(String.format("%s \nError converting toString() [%s]", e.getMessage(), getText().toString())); - } - } - - private class ChipTokenizerWrapper implements Tokenizer { - - @NonNull - private ChipTokenizer mChipTokenizer; - - public ChipTokenizerWrapper(@NonNull ChipTokenizer chipTokenizer) { - mChipTokenizer = chipTokenizer; - } - - @Override - public int findTokenStart(CharSequence text, int cursor) { - return mChipTokenizer.findTokenStart(text, cursor); - } - - @Override - public int findTokenEnd(CharSequence text, int cursor) { - return mChipTokenizer.findTokenEnd(text, cursor); - } - - @Override - public CharSequence terminateToken(CharSequence text) { - return mChipTokenizer.terminateToken(text, null); - } - } - - public interface OnChipClickListener { - - /** - * Called when a chip in this TextView is touched. This callback is triggered by the {@link MotionEvent#ACTION_UP} event. - * - * @param chip the {@link Chip} that was touched - * @param event the {@link MotionEvent} that caused the touch - */ - void onChipClick(Chip chip, MotionEvent event); - } - - public interface OnChipRemoveListener { - - /** - * Called when a chip in this TextView is removed - * - * @param chip the {@link Chip} that was removed - */ - void onChipRemove(Chip chip); - } - - private class SingleTapListener extends GestureDetector.SimpleOnGestureListener { - - /** - * @param e the {@link MotionEvent} passed to the GestureDetector - * @return true if singleTapUp (click) was detected - */ - @Override - public boolean onSingleTapUp(MotionEvent e) { - return true; - } - } -} diff --git a/nachos/src/main/java/com/hootsuite/nachos/chip/Chip.java b/nachos/src/main/java/com/hootsuite/nachos/chip/Chip.java deleted file mode 100644 index a370eb26..00000000 --- a/nachos/src/main/java/com/hootsuite/nachos/chip/Chip.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.hootsuite.nachos.chip; - - -import androidx.annotation.Nullable; - -public interface Chip { - - /** - * @return the text represented by this Chip - */ - CharSequence getText(); - - /** - * @return the data associated with this Chip or null if no data is associated with it - */ - @Nullable - Object getData(); - - /** - * @return the width of the Chip or -1 if the Chip hasn't been given the chance to calculate its width - */ - int getWidth(); - - /** - * Sets the UI state. - * - * @param stateSet one of the state constants in {@link android.view.View} - */ - void setState(int[] stateSet); -} diff --git a/nachos/src/main/java/com/hootsuite/nachos/chip/ChipCreator.java b/nachos/src/main/java/com/hootsuite/nachos/chip/ChipCreator.java deleted file mode 100644 index c38f5cff..00000000 --- a/nachos/src/main/java/com/hootsuite/nachos/chip/ChipCreator.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.hootsuite.nachos.chip; - -import android.content.Context; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.hootsuite.nachos.ChipConfiguration; - -/** - * Interface to allow the creation and configuration of chips - * - * @param The type of {@link Chip} that the implementation will create/configure - */ -public interface ChipCreator { - - /** - * Creates a chip from the given context and text. Use this method when creating a brand new chip from a piece of text. - * - * @param context the {@link Context} to use to initialize the chip - * @param text the text the Chip should represent - * @param data the data to associate with the Chip, or null to associate no data - * @return the created chip - */ - C createChip(@NonNull Context context, @NonNull CharSequence text, @Nullable Object data); - - /** - * Creates a chip from the given context and existing chip. Use this method when recreating a chip from an existing one. - * - * @param context the {@link Context} to use to initialize the chip - * @param existingChip the chip that the created chip should be based on - * @return the created chip - */ - C createChip(@NonNull Context context, @NonNull C existingChip); - - /** - * Applies the given {@link ChipConfiguration} to the given {@link Chip}. Use this method to customize the appearance/behavior of a chip before - * adding it to the text. - * - * @param chip the chip to configure - * @param chipConfiguration the configuration to apply to the chip - */ - void configureChip(@NonNull C chip, @NonNull ChipConfiguration chipConfiguration); -} diff --git a/nachos/src/main/java/com/hootsuite/nachos/chip/ChipInfo.java b/nachos/src/main/java/com/hootsuite/nachos/chip/ChipInfo.java deleted file mode 100644 index 69b8ea4c..00000000 --- a/nachos/src/main/java/com/hootsuite/nachos/chip/ChipInfo.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.hootsuite.nachos.chip; - -public class ChipInfo { - - private final CharSequence mText; - private final Object mData; - - public ChipInfo(CharSequence text, Object data) { - this.mText = text; - this.mData = data; - } - - public CharSequence getText() { - return mText; - } - - public Object getData() { - return mData; - } -} diff --git a/nachos/src/main/java/com/hootsuite/nachos/chip/ChipSpan.java b/nachos/src/main/java/com/hootsuite/nachos/chip/ChipSpan.java deleted file mode 100644 index 652f5550..00000000 --- a/nachos/src/main/java/com/hootsuite/nachos/chip/ChipSpan.java +++ /dev/null @@ -1,511 +0,0 @@ -package com.hootsuite.nachos.chip; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.drawable.Drawable; -import android.text.style.ImageSpan; - -import androidx.annotation.Dimension; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; - -import com.hootsuite.nachos.R; - -/** - * A Span that displays text and an optional icon inside of a material design chip. The chip's dimensions, colors etc. can be extensively customized - * through the various setter methods available in this class. - * The basic structure of the chip is the following: - * For chips with the icon on right: - *

- *
- *                                  (chip vertical spacing / 2)
- *                  ----------------------------------------------------------
- *                |                                                            |
- * (left margin)  |  (padding edge)   text   (padding between image)   icon    |   (right margin)
- *                |                                                            |
- *                  ----------------------------------------------------------
- *                                  (chip vertical spacing / 2)
- *
- *      
- * For chips with the icon on the left (see {@link #setShowIconOnLeft(boolean)}): - *
- *
- *                                  (chip vertical spacing / 2)
- *                  ----------------------------------------------------------
- *                |                                                            |
- * (left margin)  |   icon  (padding between image)   text   (padding edge)    |   (right margin)
- *                |                                                            |
- *                  ----------------------------------------------------------
- *                                  (chip vertical spacing / 2)
- *     
- */ -public class ChipSpan extends ImageSpan implements Chip { - - private static final float SCALE_PERCENT_OF_CHIP_HEIGHT = 0.70f; - private static final boolean ICON_ON_LEFT_DEFAULT = true; - - private int[] mStateSet = new int[]{}; - - private String mEllipsis; - - private ColorStateList mDefaultBackgroundColor; - private ColorStateList mBackgroundColor; - private int mTextColor; - private int mCornerRadius = -1; - private int mIconBackgroundColor; - - private int mTextSize = -1; - private int mPaddingEdgePx; - private int mPaddingBetweenImagePx; - private int mLeftMarginPx; - private int mRightMarginPx; - private int mMaxAvailableWidth = -1; - - private CharSequence mText; - private String mTextToDraw; - - private Drawable mIcon; - private boolean mShowIconOnLeft = ICON_ON_LEFT_DEFAULT; - - private int mChipVerticalSpacing = 0; - private int mChipHeight = -1; - private int mChipWidth = -1; - private int mIconWidth; - - private int mCachedSize = -1; - - private Object mData; - - /** - * Constructs a new ChipSpan. - * - * @param context a {@link Context} that will be used to retrieve default configurations from resource files - * @param text the text for the ChipSpan to display - * @param icon an optional icon (can be {@code null}) for the ChipSpan to display - */ - public ChipSpan(@NonNull Context context, @NonNull CharSequence text, @Nullable Drawable icon, Object data) { - super(icon); - mIcon = icon; - mText = text; - mTextToDraw = mText.toString(); - - mEllipsis = context.getString(R.string.chip_ellipsis); - - mDefaultBackgroundColor = ContextCompat.getColorStateList(context, R.color.chip_material_background); - mBackgroundColor = mDefaultBackgroundColor; - - mTextColor = ContextCompat.getColor(context, R.color.chip_default_text_color); - mIconBackgroundColor = ContextCompat.getColor(context, R.color.chip_default_icon_background_color); - - Resources resources = context.getResources(); - mPaddingEdgePx = resources.getDimensionPixelSize(R.dimen.chip_default_padding_edge); - mPaddingBetweenImagePx = resources.getDimensionPixelSize(R.dimen.chip_default_padding_between_image); - mLeftMarginPx = resources.getDimensionPixelSize(R.dimen.chip_default_left_margin); - mRightMarginPx = resources.getDimensionPixelSize(R.dimen.chip_default_right_margin); - - mData = data; - } - - /** - * Copy constructor to recreate a ChipSpan from an existing one - * - * @param context a {@link Context} that will be used to retrieve default configurations from resource files - * @param chipSpan the ChipSpan to copy - */ - public ChipSpan(@NonNull Context context, @NonNull ChipSpan chipSpan) { - this(context, chipSpan.getText(), chipSpan.getDrawable(), chipSpan.getData()); - - mDefaultBackgroundColor = chipSpan.mDefaultBackgroundColor; - mTextColor = chipSpan.mTextColor; - mIconBackgroundColor = chipSpan.mIconBackgroundColor; - mCornerRadius = chipSpan.mCornerRadius; - - mTextSize = chipSpan.mTextSize; - mPaddingEdgePx = chipSpan.mPaddingEdgePx; - mPaddingBetweenImagePx = chipSpan.mPaddingBetweenImagePx; - mLeftMarginPx = chipSpan.mLeftMarginPx; - mRightMarginPx = chipSpan.mRightMarginPx; - mMaxAvailableWidth = chipSpan.mMaxAvailableWidth; - - mShowIconOnLeft = chipSpan.mShowIconOnLeft; - - mChipVerticalSpacing = chipSpan.mChipVerticalSpacing; - mChipHeight = chipSpan.mChipHeight; - - mStateSet = chipSpan.mStateSet; - } - - @Override - public Object getData() { - return mData; - } - - /** - * Sets the height of the chip. This height should not include any extra spacing (for extra vertical spacing call {@link #setChipVerticalSpacing(int)}). - * The background of the chip will fill the full height provided here. If this method is never called, the chip will have the height of one full line - * of text by default. If {@code -1} is passed here, the chip will revert to this default behavior. - * - * @param chipHeight the height to set in pixels - */ - public void setChipHeight(int chipHeight) { - mChipHeight = chipHeight; - } - - /** - * Sets the vertical spacing to include in between chips. Half of the value set here will be placed as empty space above the chip and half the value - * will be placed as empty space below the chip. Therefore chips on consecutive lines will have the full value as vertical space in between them. - * This spacing is achieved by adjusting the font metrics used by the text view containing these chips; however it does not come into effect until - * at least one chip is created. Note that vertical spacing is dependent on having a fixed chip height (set in {@link #setChipHeight(int)}). If a - * height is not specified in that method, the value set here will be ignored. - * - * @param chipVerticalSpacing the vertical spacing to set in pixels - */ - public void setChipVerticalSpacing(int chipVerticalSpacing) { - mChipVerticalSpacing = chipVerticalSpacing; - } - - /** - * Sets the font size for the chip's text. If this method is never called, the chip text will have the same font size as the text in the TextView - * containing this chip by default. If {@code -1} is passed here, the chip will revert to this default behavior. - * - * @param size the font size to set in pixels - */ - public void setTextSize(int size) { - mTextSize = size; - invalidateCachedSize(); - } - - /** - * Sets the color for the chip's text. - * - * @param color the color to set (as a hexadecimal number in the form 0xAARRGGBB) - */ - public void setTextColor(int color) { - mTextColor = color; - } - - /** - * Sets where the icon (if an icon was provided in the constructor) will appear. - * - * @param showIconOnLeft if true, the icon will appear on the left, otherwise the icon will appear on the right - */ - public void setShowIconOnLeft(boolean showIconOnLeft) { - this.mShowIconOnLeft = showIconOnLeft; - invalidateCachedSize(); - } - - /** - * Sets the left margin. This margin will appear as empty space (it will not share the chip's background color) to the left of the chip. - * - * @param leftMarginPx the left margin to set in pixels - */ - public void setLeftMargin(int leftMarginPx) { - mLeftMarginPx = leftMarginPx; - invalidateCachedSize(); - } - - /** - * Sets the right margin. This margin will appear as empty space (it will not share the chip's background color) to the right of the chip. - * - * @param rightMarginPx the right margin to set in pixels - */ - public void setRightMargin(int rightMarginPx) { - this.mRightMarginPx = rightMarginPx; - invalidateCachedSize(); - } - - /** - * Sets the background color. To configure which color in the {@link ColorStateList} is shown you can call {@link #setState(int[])}. - * Passing {@code null} here will cause the chip to revert to it's default background. - * - * @param backgroundColor a {@link ColorStateList} containing backgrounds for different states. - * @see #setState(int[]) - */ - public void setBackgroundColor(@Nullable ColorStateList backgroundColor) { - mBackgroundColor = backgroundColor != null ? backgroundColor : mDefaultBackgroundColor; - } - - /** - * Sets the chip background corner radius. - * - * @param cornerRadius The corner radius value, in pixels. - */ - public void setCornerRadius(@Dimension int cornerRadius) { - mCornerRadius = cornerRadius; - } - - /** - * Sets the icon background color. This is the color of the circle that gets drawn behind the icon passed to the - * {@link #ChipSpan(Context, CharSequence, Drawable, Object)} constructor} - * - * @param iconBackgroundColor the icon background color to set (as a hexadecimal number in the form 0xAARRGGBB) - */ - public void setIconBackgroundColor(int iconBackgroundColor) { - mIconBackgroundColor = iconBackgroundColor; - } - - public void setMaxAvailableWidth(int maxAvailableWidth) { - mMaxAvailableWidth = maxAvailableWidth; - invalidateCachedSize(); - } - - /** - * Sets the UI state. This state will be reflected in the background color drawn for the chip. - * - * @param stateSet one of the state constants in {@link android.view.View} - * @see #setBackgroundColor(ColorStateList) - */ - @Override - public void setState(int[] stateSet) { - this.mStateSet = stateSet != null ? stateSet : new int[]{}; - } - - @Override - public CharSequence getText() { - return mText; - } - - @Override - public int getWidth() { - // If we haven't actually calculated a chip width yet just return -1, otherwise return the chip width + margins - return mChipWidth != -1 ? (mLeftMarginPx + mChipWidth + mRightMarginPx) : -1; - } - - @Override - public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) { - boolean usingFontMetrics = (fm != null); - - // Adjust the font metrics regardless of whether or not there is a cached size so that the text view can maintain its height - if (usingFontMetrics) { - adjustFontMetrics(paint, fm); - } - - if (mCachedSize == -1 && usingFontMetrics) { - mIconWidth = (mIcon != null) ? calculateChipHeight(fm.top, fm.bottom) : 0; - - int actualWidth = calculateActualWidth(paint); - mCachedSize = actualWidth; - - if (mMaxAvailableWidth != -1) { - int maxAvailableWidthMinusMargins = mMaxAvailableWidth - mLeftMarginPx - mRightMarginPx; - if (actualWidth > maxAvailableWidthMinusMargins) { - mTextToDraw = mText + mEllipsis; - - while ((calculateActualWidth(paint) > maxAvailableWidthMinusMargins) && mTextToDraw.length() > 0) { - int lastCharacterIndex = mTextToDraw.length() - mEllipsis.length() - 1; - if (lastCharacterIndex < 0) { - break; - } - mTextToDraw = mTextToDraw.substring(0, lastCharacterIndex) + mEllipsis; - } - - // Avoid a negative width - mChipWidth = Math.max(0, maxAvailableWidthMinusMargins); - mCachedSize = mMaxAvailableWidth; - } - } - } - - return mCachedSize; - } - - private int calculateActualWidth(Paint paint) { - // Only change the text size if a text size was set - if (mTextSize != -1) { - paint.setTextSize(mTextSize); - } - - int totalPadding = mPaddingEdgePx; - - // Find text width - Rect bounds = new Rect(); - paint.getTextBounds(mTextToDraw, 0, mTextToDraw.length(), bounds); - int textWidth = bounds.width(); - - if (mIcon != null) { - totalPadding += mPaddingBetweenImagePx; - } else { - totalPadding += mPaddingEdgePx; - } - - mChipWidth = totalPadding + textWidth + mIconWidth; - return getWidth(); - } - - public void invalidateCachedSize() { - mCachedSize = -1; - } - - /** - * Adjusts the provided font metrics to make it seem like the font takes up {@code mChipHeight + mChipVerticalSpacing} pixels in height. - * This effectively ensures that the TextView will have a height equal to {@code mChipHeight + mChipVerticalSpacing} + whatever padding it has set. - * In {@link #draw(Canvas, CharSequence, int, int, float, int, int, int, Paint)} the chip itself is drawn to that it is vertically centered with - * {@code mChipVerticalSpacing / 2} pixels of space above and below it - * - * @param paint the paint whose font metrics should be adjusted - * @param fm the font metrics object to populate through {@link Paint#getFontMetricsInt(Paint.FontMetricsInt)} - */ - private void adjustFontMetrics(Paint paint, Paint.FontMetricsInt fm) { - // Only actually adjust font metrics if we have a chip height set - if (mChipHeight != -1) { - paint.getFontMetricsInt(fm); - int textHeight = fm.descent - fm.ascent; - // Break up the vertical spacing in half because half will go above the chip, half will go below the chip - int halfSpacing = mChipVerticalSpacing / 2; - - // Given that the text is centered vertically within the chip, the amount of space above or below the text (inbetween the text and chip) - // is half their difference in height: - int spaceBetweenChipAndText = (mChipHeight - textHeight) / 2; - - int textTop = fm.top; - int chipTop = fm.top - spaceBetweenChipAndText; - - int textBottom = fm.bottom; - int chipBottom = fm.bottom + spaceBetweenChipAndText; - - // The text may have been taller to begin with so we take the most negative coordinate (highest up) to be the top of the content - int topOfContent = Math.min(textTop, chipTop); - // Same as above but we want the largest positive coordinate (lowest down) to be the bottom of the content - int bottomOfContent = Math.max(textBottom, chipBottom); - - // Shift the top up by halfSpacing and the bottom down by halfSpacing - int topOfContentWithSpacing = topOfContent - halfSpacing; - int bottomOfContentWithSpacing = bottomOfContent + halfSpacing; - - // Change the font metrics so that the TextView thinks the font takes up the vertical space of a chip + spacing - fm.ascent = topOfContentWithSpacing; - fm.descent = bottomOfContentWithSpacing; - fm.top = topOfContentWithSpacing; - fm.bottom = bottomOfContentWithSpacing; - } - } - - private int calculateChipHeight(int top, int bottom) { - // If a chip height was set we can return that, otherwise calculate it from top and bottom - return mChipHeight != -1 ? mChipHeight : bottom - top; - } - - @Override - public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) { - // Shift everything mLeftMarginPx to the left to create an empty space on the left (creating the margin) - x += mLeftMarginPx; - if (mChipHeight != -1) { - // If we set a chip height, adjust to vertically center chip in the line - // Adding (bottom - top) / 2 shifts the chip down so the top of it will be centered vertically - // Subtracting (mChipHeight / 2) shifts the chip back up so that the center of it will be centered vertically (as desired) - top += ((bottom - top) / 2) - (mChipHeight / 2); - bottom = top + mChipHeight; - } - - // Perform actual drawing - drawBackground(canvas, x, top, bottom, paint); - drawText(canvas, x, top, bottom, paint, mTextToDraw); - if (mIcon != null) { - drawIcon(canvas, x, top, bottom, paint); - } - } - - private void drawBackground(Canvas canvas, float x, int top, int bottom, Paint paint) { - int backgroundColor = mBackgroundColor.getColorForState(mStateSet, mBackgroundColor.getDefaultColor()); - paint.setColor(backgroundColor); - int height = calculateChipHeight(top, bottom); - RectF rect = new RectF(x, top, x + mChipWidth, bottom); - int cornerRadius = (mCornerRadius != -1) ? mCornerRadius : height / 2; - canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paint); - paint.setColor(mTextColor); - } - - private void drawText(Canvas canvas, float x, int top, int bottom, Paint paint, CharSequence text) { - if (mTextSize != -1) { - paint.setTextSize(mTextSize); - } - int height = calculateChipHeight(top, bottom); - Paint.FontMetrics fm = paint.getFontMetrics(); - - // The top value provided here is the y coordinate for the very top of the chip - // The y coordinate we are calculating is where the baseline of the text will be drawn - // Our objective is to have the midpoint between the top and baseline of the text be in line with the vertical center of the chip - // First we add height / 2 which will put the baseline at the vertical center of the chip - // Then we add half the height of the text which will lower baseline so that the midpoint is at the vertical center of the chip as desired - float adjustedY = top + ((height / 2) + ((-fm.top - fm.bottom) / 2)); - - // The x coordinate provided here is the left-most edge of the chip - // If there is no icon or the icon is on the right, then the text will start at the left-most edge, but indented with the edge padding, so we - // add mPaddingEdgePx - // If there is an icon and it's on the left, the text will start at the left-most edge, but indented by the combined width of the icon and - // the padding between the icon and text, so we add (mIconWidth + mPaddingBetweenImagePx) - float adjustedX = x + ((mIcon == null || !mShowIconOnLeft) ? mPaddingEdgePx : (mIconWidth + mPaddingBetweenImagePx)); - - canvas.drawText(text, 0, text.length(), adjustedX, adjustedY, paint); - } - - private void drawIcon(Canvas canvas, float x, int top, int bottom, Paint paint) { - drawIconBackground(canvas, x, top, bottom, paint); - drawIconBitmap(canvas, x, top, bottom, paint); - } - - private void drawIconBackground(Canvas canvas, float x, int top, int bottom, Paint paint) { - int height = calculateChipHeight(top, bottom); - - paint.setColor(mIconBackgroundColor); - - // Since it's a circle the diameter is equal to the height, so the radius == diameter / 2 == height / 2 - int radius = height / 2; - // The coordinates that get passed to drawCircle are for the center of the circle - // x is the left edge of the chip, (x + mChipWidth) is the right edge of the chip - // So the center of the circle is one radius distance from either the left or right edge (depending on which side the icon is being drawn on) - float circleX = mShowIconOnLeft ? (x + radius) : (x + mChipWidth - radius); - // The y coordinate is always just one radius distance from the top - canvas.drawCircle(circleX, top + radius, radius, paint); - - paint.setColor(mTextColor); - } - - private void drawIconBitmap(Canvas canvas, float x, int top, int bottom, Paint paint) { - int height = calculateChipHeight(top, bottom); - - // Create a scaled down version of the bitmap to fit within the circle (whose diameter == height) - Bitmap iconBitmap = Bitmap.createBitmap(mIcon.getIntrinsicWidth(), mIcon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); - Bitmap scaledIconBitMap = scaleDown(iconBitmap, (float) height * SCALE_PERCENT_OF_CHIP_HEIGHT, true); - iconBitmap.recycle(); - Canvas bitmapCanvas = new Canvas(scaledIconBitMap); - mIcon.setBounds(0, 0, bitmapCanvas.getWidth(), bitmapCanvas.getHeight()); - mIcon.draw(bitmapCanvas); - - // We are drawing a square icon inside of a circle - // The coordinates we pass to canvas.drawBitmap have to be for the top-left corner of the bitmap - // The bitmap should be inset by half of (circle width - bitmap width) - // Since it's a circle, the circle's width is equal to it's height which is equal to the chip height - float xInsetWithinCircle = (height - bitmapCanvas.getWidth()) / 2; - - // The icon x coordinate is going to be insetWithinCircle pixels away from the left edge of the circle - // If the icon is on the left, the left edge of the circle is just x - // If the icon is on the right, the left edge of the circle is x + mChipWidth - height - float iconX = mShowIconOnLeft ? (x + xInsetWithinCircle) : (x + mChipWidth - height + xInsetWithinCircle); - - // The y coordinate works the same way (only it's always from the top edge) - float yInsetWithinCircle = (height - bitmapCanvas.getHeight()) / 2; - float iconY = top + yInsetWithinCircle; - - canvas.drawBitmap(scaledIconBitMap, iconX, iconY, paint); - } - - private Bitmap scaleDown(Bitmap realImage, float maxImageSize, boolean filter) { - float ratio = Math.min(maxImageSize / realImage.getWidth(), maxImageSize / realImage.getHeight()); - int width = Math.round(ratio * realImage.getWidth()); - int height = Math.round(ratio * realImage.getHeight()); - return Bitmap.createScaledBitmap(realImage, width, height, filter); - } - - @Override - public String toString() { - return mText.toString(); - } -} diff --git a/nachos/src/main/java/com/hootsuite/nachos/chip/ChipSpanChipCreator.java b/nachos/src/main/java/com/hootsuite/nachos/chip/ChipSpanChipCreator.java deleted file mode 100644 index 8e0278f0..00000000 --- a/nachos/src/main/java/com/hootsuite/nachos/chip/ChipSpanChipCreator.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.hootsuite.nachos.chip; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.Color; - -import androidx.annotation.NonNull; - -import com.hootsuite.nachos.ChipConfiguration; - -public class ChipSpanChipCreator implements ChipCreator { - - @Override - public ChipSpan createChip(@NonNull Context context, @NonNull CharSequence text, Object data) { - return new ChipSpan(context, text, null, data); - } - - @Override - public ChipSpan createChip(@NonNull Context context, @NonNull ChipSpan existingChip) { - return new ChipSpan(context, existingChip); - } - - @Override - public void configureChip(@NonNull ChipSpan chip, @NonNull ChipConfiguration chipConfiguration) { - int chipHorizontalSpacing = chipConfiguration.getChipHorizontalSpacing(); - ColorStateList chipBackground = chipConfiguration.getChipBackground(); - int chipCornerRadius = chipConfiguration.getChipCornerRadius(); - int chipTextColor = chipConfiguration.getChipTextColor(); - int chipTextSize = chipConfiguration.getChipTextSize(); - int chipHeight = chipConfiguration.getChipHeight(); - int chipVerticalSpacing = chipConfiguration.getChipVerticalSpacing(); - int maxAvailableWidth = chipConfiguration.getMaxAvailableWidth(); - - if (chipHorizontalSpacing != -1) { - chip.setLeftMargin(chipHorizontalSpacing / 2); - chip.setRightMargin(chipHorizontalSpacing / 2); - } - if (chipBackground != null) { - chip.setBackgroundColor(chipBackground); - } - if (chipCornerRadius != -1) { - chip.setCornerRadius(chipCornerRadius); - } - if (chipTextColor != Color.TRANSPARENT) { - chip.setTextColor(chipTextColor); - } - if (chipTextSize != -1) { - chip.setTextSize(chipTextSize); - } - if (chipHeight != -1) { - chip.setChipHeight(chipHeight); - } - if (chipVerticalSpacing != -1) { - chip.setChipVerticalSpacing(chipVerticalSpacing); - } - if (maxAvailableWidth != -1) { - chip.setMaxAvailableWidth(maxAvailableWidth); - } - } -} diff --git a/nachos/src/main/java/com/hootsuite/nachos/terminator/ChipTerminatorHandler.java b/nachos/src/main/java/com/hootsuite/nachos/terminator/ChipTerminatorHandler.java deleted file mode 100644 index f97d430e..00000000 --- a/nachos/src/main/java/com/hootsuite/nachos/terminator/ChipTerminatorHandler.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.hootsuite.nachos.terminator; - -import android.text.Editable; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.hootsuite.nachos.tokenizer.ChipTokenizer; - -import java.util.Map; - -/** - * This interface is used to handle the management of characters that should trigger the creation of chips in a text view. - * - * @see ChipTokenizer - */ -public interface ChipTerminatorHandler { - - /** - * When a chip terminator character is encountered in newly inserted text, all tokens in the whole text view will be chipified - */ - int BEHAVIOR_CHIPIFY_ALL = 0; - - /** - * When a chip terminator character is encountered in newly inserted text, only the current token (that in which the chip terminator character - * was found) will be chipified. This token may extend beyond where the chip terminator character was located. - */ - int BEHAVIOR_CHIPIFY_CURRENT_TOKEN = 1; - - /** - * When a chip terminator character is encountered in newly inserted text, only the text from the previous chip up until the chip terminator - * character will be chipified. This may not be an entire token. - */ - int BEHAVIOR_CHIPIFY_TO_TERMINATOR = 2; - - /** - * Constant for use with {@link #setPasteBehavior(int)}. Use this if a paste should behave the same as a standard text input (the chip temrinators - * will all behave according to their pre-determined behavior set through {@link #addChipTerminator(char, int)} or {@link #setChipTerminators(Map)}). - */ - int PASTE_BEHAVIOR_USE_DEFAULT = -1; - - /** - * Sets all the characters that will be marked as chip terminators. This will replace any previously set chip terminators. - * - * @param chipTerminators a map of characters to be marked as chip terminators to behaviors that describe how to respond to the characters, or null - * to remove all chip terminators - */ - void setChipTerminators(@Nullable Map chipTerminators); - - /** - * Adds a character as a chip terminator. When the provided character is encountered in entered text, the nearby text will be chipified according - * to the behavior provided here. - * {@code behavior} Must be one of: - *
    - *
  • {@link #BEHAVIOR_CHIPIFY_ALL}
  • - *
  • {@link #BEHAVIOR_CHIPIFY_CURRENT_TOKEN}
  • - *
  • {@link #BEHAVIOR_CHIPIFY_TO_TERMINATOR}
  • - *
- * - * @param character the character to mark as a chip terminator - * @param behavior the behavior describing how to respond to the chip terminator - */ - void addChipTerminator(char character, int behavior); - - /** - * Customizes the way paste events are handled. - * If one of: - *
    - *
  • {@link #BEHAVIOR_CHIPIFY_ALL}
  • - *
  • {@link #BEHAVIOR_CHIPIFY_CURRENT_TOKEN}
  • - *
  • {@link #BEHAVIOR_CHIPIFY_TO_TERMINATOR}
  • - *
- * is passed, all chip terminators will be handled with that behavior when a paste event occurs. - * If {@link #PASTE_BEHAVIOR_USE_DEFAULT} is passed, whatever behavior is configured for a particular chip terminator - * (through {@link #setChipTerminators(Map)} or {@link #addChipTerminator(char, int)} will be used for that chip terminator - * - * @param pasteBehavior the behavior to use on a paste event - */ - void setPasteBehavior(int pasteBehavior); - - /** - * Parses the provided text looking for characters marked as chip terminators through {@link #addChipTerminator(char, int)} and {@link #setChipTerminators(Map)}. - * The provided {@link Editable} will be modified if chip terminators are encountered. - * - * @param tokenizer the {@link ChipTokenizer} to use to identify and chipify tokens in the text - * @param text the text in which to search for chip terminators tokens to be chipped - * @param start the index at which to begin looking for chip terminators (inclusive) - * @param end the index at which to end looking for chip terminators (exclusive) - * @param isPasteEvent true if this handling is for a paste event in which case the behavior set in {@link #setPasteBehavior(int)} will be used, - * otherwise false - * @return an non-negative integer indicating the index where the cursor (selection) should be placed once the handling is complete, - * or a negative integer indicating that the cursor should not be moved. - */ - int findAndHandleChipTerminators(@NonNull ChipTokenizer tokenizer, @NonNull Editable text, int start, int end, boolean isPasteEvent); -} diff --git a/nachos/src/main/java/com/hootsuite/nachos/terminator/DefaultChipTerminatorHandler.java b/nachos/src/main/java/com/hootsuite/nachos/terminator/DefaultChipTerminatorHandler.java deleted file mode 100644 index 75cb9b0d..00000000 --- a/nachos/src/main/java/com/hootsuite/nachos/terminator/DefaultChipTerminatorHandler.java +++ /dev/null @@ -1,115 +0,0 @@ -package com.hootsuite.nachos.terminator; - -import android.text.Editable; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.hootsuite.nachos.tokenizer.ChipTokenizer; - -import java.util.HashMap; -import java.util.Map; - -public class DefaultChipTerminatorHandler implements ChipTerminatorHandler { - - @Nullable - private Map mChipTerminators; - private int mPasteBehavior = BEHAVIOR_CHIPIFY_TO_TERMINATOR; - - @Override - public void setChipTerminators(@Nullable Map chipTerminators) { - mChipTerminators = chipTerminators; - } - - @Override - public void addChipTerminator(char character, int behavior) { - if (mChipTerminators == null) { - mChipTerminators = new HashMap<>(); - } - - mChipTerminators.put(character, behavior); - } - - @Override - public void setPasteBehavior(int pasteBehavior) { - mPasteBehavior = pasteBehavior; - } - - @Override - public int findAndHandleChipTerminators(@NonNull ChipTokenizer tokenizer, @NonNull Editable text, int start, int end, boolean isPasteEvent) { - // If we don't have a tokenizer or any chip terminators, there's nothing to look for - if (mChipTerminators == null) { - return -1; - } - - TextIterator textIterator = new TextIterator(text, start, end); - int selectionIndex = -1; - - characterLoop: - while (textIterator.hasNextCharacter()) { - char theChar = textIterator.nextCharacter(); - if (isChipTerminator(theChar)) { - int behavior = (isPasteEvent && mPasteBehavior != PASTE_BEHAVIOR_USE_DEFAULT) ? mPasteBehavior : mChipTerminators.get(theChar); - int newSelection = -1; - switch (behavior) { - case BEHAVIOR_CHIPIFY_ALL: - selectionIndex = handleChipifyAll(textIterator, tokenizer); - break characterLoop; - case BEHAVIOR_CHIPIFY_CURRENT_TOKEN: - newSelection = handleChipifyCurrentToken(textIterator, tokenizer); - break; - case BEHAVIOR_CHIPIFY_TO_TERMINATOR: - newSelection = handleChipifyToTerminator(textIterator, tokenizer); - break; - } - - if (newSelection != -1) { - selectionIndex = newSelection; - } - } - } - - return selectionIndex; - } - - private int handleChipifyAll(TextIterator textIterator, ChipTokenizer tokenizer) { - textIterator.deleteCharacter(true); - tokenizer.terminateAllTokens(textIterator.getText()); - return textIterator.totalLength(); - } - - private int handleChipifyCurrentToken(TextIterator textIterator, ChipTokenizer tokenizer) { - textIterator.deleteCharacter(true); - Editable text = textIterator.getText(); - int index = textIterator.getIndex(); - int tokenStart = tokenizer.findTokenStart(text, index); - int tokenEnd = tokenizer.findTokenEnd(text, index); - if (tokenStart < tokenEnd) { - CharSequence chippedText = tokenizer.terminateToken(text.subSequence(tokenStart, tokenEnd), null); - textIterator.replace(tokenStart, tokenEnd, chippedText); - return tokenStart + chippedText.length(); - } - return -1; - } - - private int handleChipifyToTerminator(TextIterator textIterator, ChipTokenizer tokenizer) { - Editable text = textIterator.getText(); - int index = textIterator.getIndex(); - if (index > 0) { - int tokenStart = tokenizer.findTokenStart(text, index); - if (tokenStart < index) { - CharSequence chippedText = tokenizer.terminateToken(text.subSequence(tokenStart, index), null); - textIterator.replace(tokenStart, index + 1, chippedText); - } else { - textIterator.deleteCharacter(false); - } - } else { - textIterator.deleteCharacter(false); - } - return -1; - } - - private boolean isChipTerminator(char character) { - return mChipTerminators != null && mChipTerminators.keySet().contains(character); - } -} diff --git a/nachos/src/main/java/com/hootsuite/nachos/terminator/TextIterator.java b/nachos/src/main/java/com/hootsuite/nachos/terminator/TextIterator.java deleted file mode 100644 index 7c25d4c9..00000000 --- a/nachos/src/main/java/com/hootsuite/nachos/terminator/TextIterator.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.hootsuite.nachos.terminator; - -import android.text.Editable; - -public class TextIterator { - - private Editable mText; - private int mStart; - private int mEnd; - - private int mIndex; - - public TextIterator(Editable text, int start, int end) { - mText = text; - mStart = start; - mEnd = end; - - mIndex = mStart - 1; // Subtract 1 so that the first call to nextCharacter() will return the first character - } - - public int totalLength() { - return mText.length(); - } - - public int windowLength() { - return mEnd - mStart; - } - - public Editable getText() { - return mText; - } - - public int getIndex() { - return mIndex; - } - - public boolean hasNextCharacter() { - return (mIndex + 1) < mEnd; - } - - public char nextCharacter() { - mIndex++; - return mText.charAt(mIndex); - } - - public void deleteCharacter(boolean maintainIndex) { - mText.replace(mIndex, mIndex + 1, ""); - if (!maintainIndex) { - mIndex--; - } - mEnd--; - } - - public void replace(int replaceStart, int replaceEnd, CharSequence chippedText) { - mText.replace(replaceStart, replaceEnd, chippedText); - - // Update indexes - int newLength = chippedText.length(); - int oldLength = replaceEnd - replaceStart; - mIndex = replaceStart + newLength - 1; - mEnd += newLength - oldLength; - } -} diff --git a/nachos/src/main/java/com/hootsuite/nachos/tokenizer/BaseChipTokenizer.java b/nachos/src/main/java/com/hootsuite/nachos/tokenizer/BaseChipTokenizer.java deleted file mode 100644 index ba946b5b..00000000 --- a/nachos/src/main/java/com/hootsuite/nachos/tokenizer/BaseChipTokenizer.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.hootsuite.nachos.tokenizer; - -import android.text.Editable; -import android.text.Spanned; -import android.util.Pair; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.hootsuite.nachos.ChipConfiguration; -import com.hootsuite.nachos.chip.Chip; - -import java.util.ArrayList; -import java.util.List; - -/** - * Base implementation of the {@link ChipTokenizer} interface that performs no actions and returns default values. - * This class allows for the easy creation of a ChipTokenizer that only implements some of the methods of the interface. - */ -public abstract class BaseChipTokenizer implements ChipTokenizer { - - @Override - public void applyConfiguration(Editable text, ChipConfiguration chipConfiguration) { - // Do nothing - } - - @Override - public int findTokenStart(CharSequence charSequence, int i) { - // Do nothing - return 0; - } - - @Override - public int findTokenEnd(CharSequence charSequence, int i) { - // Do nothing - return 0; - } - - @NonNull - @Override - public List> findAllTokens(CharSequence text) { - // Do nothing - return new ArrayList<>(); - } - - @Override - public CharSequence terminateToken(CharSequence charSequence, @Nullable Object data) { - // Do nothing - return charSequence; - } - - @Override - public void terminateAllTokens(Editable text) { - // Do nothing - } - - @Override - public int findChipStart(Chip chip, Spanned text) { - // Do nothing - return 0; - } - - @Override - public int findChipEnd(Chip chip, Spanned text) { - // Do nothing - return 0; - } - - @NonNull - @Override - public Chip[] findAllChips(int start, int end, Spanned text) { - return new Chip[]{}; - } - - @Override - public void revertChipToToken(Chip chip, Editable text) { - // Do nothing - } - - @Override - public void deleteChip(Chip chip, Editable text) { - // Do nothing - } - - @Override - public void deleteChipAndPadding(Chip chip, Editable text) { - // Do nothing - } -} diff --git a/nachos/src/main/java/com/hootsuite/nachos/tokenizer/ChipTokenizer.java b/nachos/src/main/java/com/hootsuite/nachos/tokenizer/ChipTokenizer.java deleted file mode 100644 index 32d8f061..00000000 --- a/nachos/src/main/java/com/hootsuite/nachos/tokenizer/ChipTokenizer.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.hootsuite.nachos.tokenizer; - -import android.text.Editable; -import android.text.Spanned; -import android.util.Pair; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.hootsuite.nachos.ChipConfiguration; -import com.hootsuite.nachos.chip.Chip; - -import java.util.List; - -/** - * An extension of {@link android.widget.MultiAutoCompleteTextView.Tokenizer Tokenizer} that provides extra support - * for chipification. - *

- * In the context of this interface, a token is considered to be plain (non-chipped) text. Once a token is terminated it becomes or contains a chip. - *

- *

- * The CharSequences passed to the ChipTokenizer methods may contain both chipped text - * and plain text so the tokenizer must have some method of distinguishing between the two (e.g. using a delimeter character. - * The {@link #terminateToken(CharSequence, Object)} method is where a chip can be formed and returned to replace the plain text. - * Whatever class the implementation deems to represent a chip, must implement the {@link Chip} interface. - *

- * - * @see SpanChipTokenizer - */ -public interface ChipTokenizer { - - /** - * Configures this ChipTokenizer to produce chips with the provided attributes. For each of these attributes, {@code -1} or {@code null} may be - * passed to indicate that the attribute may be ignored. - *

- * This will also apply the provided {@link ChipConfiguration} to any existing chips in the provided text. - *

- * - * @param text the text in which to search for existing chips to apply the configuration to - * @param chipConfiguration a {@link ChipConfiguration} containing customizations for the chips produced by this class - */ - void applyConfiguration(Editable text, ChipConfiguration chipConfiguration); - - /** - * Returns the start of the token that ends at offset - * cursor within text. - */ - int findTokenStart(CharSequence text, int cursor); - - /** - * Returns the end of the token (minus trailing punctuation) - * that begins at offset cursor within text. - */ - int findTokenEnd(CharSequence text, int cursor); - - /** - * Searches through {@code text} for any tokens. - * - * @param text the text in which to search for un-terminated tokens - * @return a list of {@link Pair}s of the form (startIndex, endIndex) containing the locations of all - * unterminated tokens - */ - @NonNull - List> findAllTokens(CharSequence text); - - /** - * Returns text, modified, if necessary, to ensure that - * it ends with a token terminator (for example a space or comma). - */ - CharSequence terminateToken(CharSequence text, @Nullable Object data); - - /** - * Terminates (converts from token into chip) all unterminated tokens in the provided text. - * This method CAN alter the provided text. - * - * @param text the text in which to terminate all tokens - */ - void terminateAllTokens(Editable text); - - /** - * Finds the index of the first character in {@code text} that is a part of {@code chip} - * - * @param chip the chip whose start should be found - * @param text the text in which to search for the start of {@code chip} - * @return the start index of the chip - */ - int findChipStart(Chip chip, Spanned text); - - /** - * Finds the index of the character after the last character in {@code text} that is a part of {@code chip} - * - * @param chip the chip whose end should be found - * @param text the text in which to search for the end of {@code chip} - * @return the end index of the chip - */ - int findChipEnd(Chip chip, Spanned text); - - /** - * Searches through {@code text} for any chips - * - * @param start index to start looking for terminated tokens (inclusive) - * @param end index to end looking for terminated tokens (exclusive) - * @param text the text in which to search for terminated tokens - * @return a list of objects implementing the {@link Chip} interface to represent the terminated tokens - */ - @NonNull - Chip[] findAllChips(int start, int end, Spanned text); - - /** - * Effectively does the opposite of {@link #terminateToken(CharSequence, Object)} by reverting the provided chip back into a token. - * This method CAN alter the provided text. - * - * @param chip the chip to revert into a token - * @param text the text in which the chip resides - */ - void revertChipToToken(Chip chip, Editable text); - - /** - * Removes a chip and any text it encompasses from {@code text}. This method CAN alter the provided text. - * - * @param chip the chip to remove - * @param text the text to remove the chip from - */ - void deleteChip(Chip chip, Editable text); - - /** - * Removes a chip, any text it encompasses AND any padding text (such as spaces) that may have been inserted when the chip was created in - * {@link #terminateToken(CharSequence, Object)} or after. This method CAN alter the provided text. - * - * @param chip the chip to remove - * @param text the text to remove the chip and padding from - */ - void deleteChipAndPadding(Chip chip, Editable text); -} diff --git a/nachos/src/main/java/com/hootsuite/nachos/tokenizer/SpanChipTokenizer.java b/nachos/src/main/java/com/hootsuite/nachos/tokenizer/SpanChipTokenizer.java deleted file mode 100644 index 79410e20..00000000 --- a/nachos/src/main/java/com/hootsuite/nachos/tokenizer/SpanChipTokenizer.java +++ /dev/null @@ -1,248 +0,0 @@ -package com.hootsuite.nachos.tokenizer; - -import android.content.Context; -import android.text.Editable; -import android.text.SpannableString; -import android.text.Spanned; -import android.util.Pair; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.hootsuite.nachos.ChipConfiguration; -import com.hootsuite.nachos.chip.Chip; -import com.hootsuite.nachos.chip.ChipCreator; -import com.hootsuite.nachos.chip.ChipSpan; - -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -/** - * A default implementation of {@link ChipTokenizer}. - * This implementation does the following: - *
    - *
  • Surrounds each token with a space and the Unit Separator ASCII control character (31) - See the diagram below - *
      - *
    • The spaces are included so that android keyboards can distinguish the chips as different words and provide accurate - * autocorrect suggestions
    • - *
    - *
  • - *
  • Replaces each token with a {@link ChipSpan} containing the same text, once the token terminates
  • - *
  • Uses the values passed to {@link #applyConfiguration(Editable, ChipConfiguration)} to configure any ChipSpans that get created
  • - *
- * Each terminated token will therefore look like the following (this is what will be returned from {@link #terminateToken(CharSequence, Object)}): - *
- *  -----------------------------------------------------------
- *  | SpannableString                                         |
- *  |   ----------------------------------------------------  |
- *  |   | ChipSpan                                         |  |
- *  |   |                                                  |  |
- *  |   |  space   separator    text    separator   space  |  |
- *  |   |                                                  |  |
- *  |   ----------------------------------------------------  |
- *  -----------------------------------------------------------
- * 
- * - * @see ChipSpan - */ -public class SpanChipTokenizer implements ChipTokenizer { - - /** - * The character used to separate chips internally is the US (Unit Separator) ASCII control character. - * This character is used because it's untypable so we have complete control over when chips are created. - */ - public static final char CHIP_SPAN_SEPARATOR = 31; - public static final char AUTOCORRECT_SEPARATOR = ' '; - - private Context mContext; - - @Nullable - private ChipConfiguration mChipConfiguration; - @NonNull - private ChipCreator mChipCreator; - @NonNull - private Class mChipClass; - - private Comparator> mReverseTokenIndexesSorter = new Comparator>() { - @Override - public int compare(Pair lhs, Pair rhs) { - return rhs.first - lhs.first; - } - }; - - public SpanChipTokenizer(Context context, @NonNull ChipCreator chipCreator, @NonNull Class chipClass) { - mContext = context; - mChipCreator = chipCreator; - mChipClass = chipClass; - } - - @Override - public void applyConfiguration(Editable text, ChipConfiguration chipConfiguration) { - mChipConfiguration = chipConfiguration; - - for (C chip : findAllChips(0, text.length(), text)) { - // Recreate the chips with the new configuration - int chipStart = findChipStart(chip, text); - deleteChip(chip, text); - text.insert(chipStart, terminateToken(mChipCreator.createChip(mContext, chip))); - } - } - - @Override - public int findTokenStart(CharSequence text, int cursor) { - int i = cursor; - - // Work backwards until we find a CHIP_SPAN_SEPARATOR - while (i > 0 && text.charAt(i - 1) != CHIP_SPAN_SEPARATOR) { - i--; - } - // Work forwards to skip over any extra whitespace at the beginning of the token - while (i > 0 && i < text.length() && Character.isWhitespace(text.charAt(i))) { - i++; - } - return i; - } - - @Override - public int findTokenEnd(CharSequence text, int cursor) { - int i = cursor; - int len = text.length(); - - // Work forwards till we find a CHIP_SPAN_SEPARATOR - while (i < len) { - if (text.charAt(i) == CHIP_SPAN_SEPARATOR) { - return (i - 1); // subtract one because the CHIP_SPAN_SEPARATOR will be preceded by a space - } else { - i++; - } - } - return len; - } - - @NonNull - @Override - public List> findAllTokens(CharSequence text) { - List> unterminatedTokens = new ArrayList<>(); - - boolean insideChip = false; - // Iterate backwards through the text (to avoid messing up indexes) - for (int index = text.length() - 1; index >= 0; index--) { - char theCharacter = text.charAt(index); - - // Every time we hit a CHIP_SPAN_SEPARATOR character we switch from being inside to outside - // or outside to inside a chip - // This check must happen before the whitespace check because CHIP_SPAN_SEPARATOR is considered a whitespace character - if (theCharacter == CHIP_SPAN_SEPARATOR) { - insideChip = !insideChip; - continue; - } - - // Completely skip over whitespace - if (Character.isWhitespace(theCharacter)) { - continue; - } - - // If we're ever outside a chip, see if the text we're in is a viable token for chipification - if (!insideChip) { - int tokenStart = findTokenStart(text, index); - int tokenEnd = findTokenEnd(text, index); - - // Can only actually be chipified if there's at least one character between them - if (tokenEnd - tokenStart >= 1) { - unterminatedTokens.add(new Pair<>(tokenStart, tokenEnd)); - index = tokenStart; - } - } - } - return unterminatedTokens; - } - - @Override - public CharSequence terminateToken(CharSequence text, @Nullable Object data) { - // Remove leading/trailing whitespace - CharSequence trimmedText = text.toString().trim(); - return terminateToken(mChipCreator.createChip(mContext, trimmedText, data)); - } - - private CharSequence terminateToken(C chip) { - if (chip == null) - return new SpannableString(""); - // Surround the text with CHIP_SPAN_SEPARATOR and spaces - // The spaces allow autocorrect to correctly identify words - String chipSeparator = Character.toString(CHIP_SPAN_SEPARATOR); - String autoCorrectSeparator = Character.toString(AUTOCORRECT_SEPARATOR); - CharSequence textWithSeparator = autoCorrectSeparator + chipSeparator + chip.getText() + chipSeparator + autoCorrectSeparator; - - // Build the container object to house the ChipSpan and space - SpannableString spannableString = new SpannableString(textWithSeparator); - - // Attach the ChipSpan - if (mChipConfiguration != null) { - mChipCreator.configureChip(chip, mChipConfiguration); - } - spannableString.setSpan(chip, 0, textWithSeparator.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - - return spannableString; - } - - @Override - public void terminateAllTokens(Editable text) { - List> unterminatedTokens = findAllTokens(text); - // Sort in reverse order (so index changes don't affect anything) - Collections.sort(unterminatedTokens, mReverseTokenIndexesSorter); - for (Pair indexes : unterminatedTokens) { - int start = indexes.first; - int end = indexes.second; - CharSequence textToChip = text.subSequence(start, end); - CharSequence chippedText = terminateToken(textToChip, null); - text.replace(start, end, chippedText); - } - } - - @Override - public int findChipStart(Chip chip, Spanned text) { - return text.getSpanStart(chip); - } - - @Override - public int findChipEnd(Chip chip, Spanned text) { - return text.getSpanEnd(chip); - } - - @SuppressWarnings("unchecked") - @NonNull - @Override - public C[] findAllChips(int start, int end, Spanned text) { - C[] spansArray = text.getSpans(start, end, mChipClass); - return (spansArray != null) ? spansArray : (C[]) Array.newInstance(mChipClass, 0); - } - - @Override - public void revertChipToToken(Chip chip, Editable text) { - int chipStart = findChipStart(chip, text); - int chipEnd = findChipEnd(chip, text); - text.removeSpan(chip); - text.replace(chipStart, chipEnd, chip.getText()); - } - - @Override - public void deleteChip(Chip chip, Editable text) { - int chipStart = findChipStart(chip, text); - int chipEnd = findChipEnd(chip, text); - text.removeSpan(chip); - // On the emulator for some reason the text automatically gets deleted and chipStart and chipEnd end up both being -1, so in that case we - // don't need to call text.delete(...) - if (chipStart != chipEnd) { - text.delete(chipStart, chipEnd); - } - } - - @Override - public void deleteChipAndPadding(Chip chip, Editable text) { - // This implementation does not add any extra padding outside of the span so we can just delete the chip normally - deleteChip(chip, text); - } -} diff --git a/nachos/src/main/java/com/hootsuite/nachos/validator/ChipifyingNachoValidator.java b/nachos/src/main/java/com/hootsuite/nachos/validator/ChipifyingNachoValidator.java deleted file mode 100644 index fa1aa571..00000000 --- a/nachos/src/main/java/com/hootsuite/nachos/validator/ChipifyingNachoValidator.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.hootsuite.nachos.validator; - -import android.text.SpannableStringBuilder; -import android.util.Pair; - -import androidx.annotation.NonNull; - -import com.hootsuite.nachos.tokenizer.ChipTokenizer; - -import java.util.List; - -/** - * A {@link NachoValidator} that deems text to be invalid if it contains - * unterminated tokens and fixes the text by chipifying all the unterminated tokens. - */ -public class ChipifyingNachoValidator implements NachoValidator { - - @Override - public boolean isValid(@NonNull ChipTokenizer chipTokenizer, CharSequence text) { - - // The text is considered valid if there are no unterminated tokens (everything is a chip) - List> unterminatedTokens = chipTokenizer.findAllTokens(text); - return unterminatedTokens.isEmpty(); - } - - @Override - public CharSequence fixText(@NonNull ChipTokenizer chipTokenizer, CharSequence invalidText) { - SpannableStringBuilder newText = new SpannableStringBuilder(invalidText); - chipTokenizer.terminateAllTokens(newText); - return newText; - } -} diff --git a/nachos/src/main/java/com/hootsuite/nachos/validator/IllegalCharacterIdentifier.java b/nachos/src/main/java/com/hootsuite/nachos/validator/IllegalCharacterIdentifier.java deleted file mode 100644 index 76f3f858..00000000 --- a/nachos/src/main/java/com/hootsuite/nachos/validator/IllegalCharacterIdentifier.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.hootsuite.nachos.validator; - -public interface IllegalCharacterIdentifier { - boolean isCharacterIllegal(Character c); -} diff --git a/nachos/src/main/java/com/hootsuite/nachos/validator/NachoValidator.java b/nachos/src/main/java/com/hootsuite/nachos/validator/NachoValidator.java deleted file mode 100644 index 6c798e7c..00000000 --- a/nachos/src/main/java/com/hootsuite/nachos/validator/NachoValidator.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.hootsuite.nachos.validator; - - -import androidx.annotation.NonNull; - -import com.hootsuite.nachos.tokenizer.ChipTokenizer; - -/** - * Interface used to ensure that a given CharSequence complies to a particular format. - */ -public interface NachoValidator { - - /** - * Validates the specified text. - * - * @return true If the text currently in the text editor is valid. - * @see #fixText(ChipTokenizer, CharSequence) - */ - boolean isValid(@NonNull ChipTokenizer chipTokenizer, CharSequence text); - - /** - * Corrects the specified text to make it valid. - * - * @param invalidText A string that doesn't pass validation: isValid(invalidText) - * returns false - * @return A string based on invalidText such as invoking isValid() on it returns true. - * @see #isValid(ChipTokenizer, CharSequence) - */ - CharSequence fixText(@NonNull ChipTokenizer chipTokenizer, CharSequence invalidText); -} diff --git a/nachos/src/main/project.properties b/nachos/src/main/project.properties deleted file mode 100644 index 70c5769d..00000000 --- a/nachos/src/main/project.properties +++ /dev/null @@ -1,2 +0,0 @@ -# See https://github.com/robolectric/robolectric/issues/1334 -android.library.reference.1=../../build/intermediates/exploded-aar/com.android.support/appcompat-v7/22.2.1' \ No newline at end of file diff --git a/nachos/src/main/res/color/chip_material_background.xml b/nachos/src/main/res/color/chip_material_background.xml deleted file mode 100644 index ec381b24..00000000 --- a/nachos/src/main/res/color/chip_material_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/nachos/src/main/res/values/attrs.xml b/nachos/src/main/res/values/attrs.xml deleted file mode 100644 index dcd632af..00000000 --- a/nachos/src/main/res/values/attrs.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/nachos/src/main/res/values/colors.xml b/nachos/src/main/res/values/colors.xml deleted file mode 100644 index a718d37d..00000000 --- a/nachos/src/main/res/values/colors.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - #DE000000 - #517FA4 - \ No newline at end of file diff --git a/nachos/src/main/res/values/dimens.xml b/nachos/src/main/res/values/dimens.xml deleted file mode 100644 index 8df9e5ad..00000000 --- a/nachos/src/main/res/values/dimens.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - 12dp - 8dp - 0dp - 0dp - \ No newline at end of file diff --git a/nachos/src/main/res/values/strings.xml b/nachos/src/main/res/values/strings.xml deleted file mode 100644 index 6c2b508b..00000000 --- a/nachos/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/nachos/src/main/res/values/styles.xml b/nachos/src/main/res/values/styles.xml deleted file mode 100644 index 5ef64347..00000000 --- a/nachos/src/main/res/values/styles.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 955021f9..f2de53d8 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,7 +3,7 @@ include ':wear' include ':codegen' include ':annotation' rootProject.name='Szkolny.eu' -include ':app', ':agendacalendarview', ':mhttp', ':szkolny-font', ':nachos' +include ':app', ':agendacalendarview', ':mhttp', ':szkolny-font' /* include ':Navigation' project(':Navigation').projectDir = new File(settingsDir, '../Navigation/navlib')*/