Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Change Logs

## 3.1.1
- New : Add `Position` and `PhotoEditor.addText(..., position)` overloads for initial text placement
- New : Sample app now lets users tap the image to place text before entering it
- Test : Add targeted placement coverage in `GraphicManagerTest` and update sample app test flow for tap-to-place text
- Docs : Update `README.md` with developer API usage and end-user sample app instructions, including adding multiple text labels
- Fixed : Mirror upstream issue #589 in this fork by removing the "always centered" text insertion limitation

## 0.1.1
- Change : `app:src="@drawable/got_s"` to `app:photo_src="@drawable/got_s"` in `PhotoEditorView`

Expand Down Expand Up @@ -106,4 +113,4 @@ new TextStyleBuilder()
- Fixed : Clearing redo stack after brush drawing
- Fixed : Using eraser size when in erasing mode
- Change : Bumped Kotlin to 2.0.0, AGP to 8.5.1, Gradle to 8.9, target SDK to 34
- Removed : Non-functional `OnMultiTouchListener` interface
- Removed : Non-functional `OnMultiTouchListener` interface
5 changes: 4 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
2. Fork the project.
3. Create a branch with name PE-[#Issue No.] Ex : PE-146
4. Make required changes and commit to that branch.
5. Generate pull request. Mention all the required description regarding changes you made.
5. Before opening a pull request, run the broader local verification gates used by CI:
- `./gradlew check`
- `./gradlew build`
6. Generate pull request. Mention all the required description regarding changes you made.

Happy coding.:-)
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,20 @@ It will take default fonts provided in the builder. If we want different fonts f

`mPhotoEditor.addText(mTypeface,inputText, colorCode);`

If you want the text to start at a specific location instead of the default centered position, use the overload with a `Position`:

`mPhotoEditor.addText(inputText, colorCode, Position(80, 160));`

Comment on lines +162 to +165
After insertion, text can still be dragged, rotated, and scaled on the canvas.

In the sample app, the end-user flow is now:

1. Tap `Text`.
2. Tap the image where the label should appear.
3. Enter the text in the dialog.

Repeat the same flow to add multiple text labels in different positions on the image.

In order to edit the text we need the view, which we will receive in our PhotoEditor callback. This callback will trigger when we **Long Press** the added text

```java
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ class EditImageActivityTest {

// Add a text to the image.
Espresso.onView(ViewMatchers.withText(R.string.label_text)).perform(ViewActions.click())
Espresso.onView(ViewMatchers.withId(R.id.photoEditorView)).perform(ViewActions.click())
Espresso.onView(ViewMatchers.withId(R.id.add_text_edit_text)).perform(ViewActions.click())
Espresso.onView(ViewMatchers.withId(R.id.add_text_edit_text))
.perform(ViewActions.typeText("Test Text"))
Expand Down Expand Up @@ -304,6 +305,7 @@ class EditImageActivityTest {
// Open the emoji menu (delay to give time to load lower menu)
Thread.sleep(2000)
Espresso.onView(ViewMatchers.withText(R.string.label_text)).perform(ViewActions.click())
Espresso.onView(ViewMatchers.withId(R.id.photoEditorView)).perform(ViewActions.click())
Espresso.onView(ViewMatchers.withId(R.id.add_text_edit_text)).perform(ViewActions.click())

// Type the text (delay to allow keyboard to load)
Expand Down Expand Up @@ -413,4 +415,4 @@ class EditImageActivityTest {
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import ja.burhanrashid52.photoeditor.OnPhotoEditorListener
import ja.burhanrashid52.photoeditor.PhotoEditor
import ja.burhanrashid52.photoeditor.PhotoEditorView
import ja.burhanrashid52.photoeditor.PhotoFilter
import ja.burhanrashid52.photoeditor.Position
import ja.burhanrashid52.photoeditor.SaveFileResult
import ja.burhanrashid52.photoeditor.SaveSettings
import ja.burhanrashid52.photoeditor.TextStyleBuilder
Expand Down Expand Up @@ -68,11 +69,13 @@ class EditImageActivity : BaseActivity(), OnPhotoEditorListener, View.OnClickLis
private lateinit var mRvFilters: RecyclerView
private lateinit var mImgUndo: View
private lateinit var mImgRedo: View
private lateinit var mTextPlacementOverlay: View
private val mEditingToolsAdapter = EditingToolsAdapter(this)
private val mFilterViewAdapter = FilterViewAdapter(this)
private lateinit var mRootView: ConstraintLayout
private val mConstraintSet = ConstraintSet()
private var mIsFilterVisible = false
private var mIsTextPlacementPending = false

@VisibleForTesting
var mSaveImageUri: Uri? = null
Expand Down Expand Up @@ -157,6 +160,16 @@ class EditImageActivity : BaseActivity(), OnPhotoEditorListener, View.OnClickLis

private fun initViews() {
mPhotoEditorView = findViewById(R.id.photoEditorView)
mTextPlacementOverlay = findViewById(R.id.viewTextPlacementOverlay)
mTextPlacementOverlay.setOnTouchListener { _, event ->
if (mIsTextPlacementPending && event.action == MotionEvent.ACTION_UP) {
clearPendingTextPlacement()
showTextEditorDialog(Position(event.x.toInt(), event.y.toInt()))
true
} else {
false
}
}
mTxtCurrentTool = findViewById(R.id.txtCurrentTool)
mRvTools = findViewById(R.id.rvConstraintTools)
mRvFilters = findViewById(R.id.rvFilterView)
Expand Down Expand Up @@ -187,6 +200,7 @@ class EditImageActivity : BaseActivity(), OnPhotoEditorListener, View.OnClickLis
}

override fun onEditTextChangeListener(rootView: View, text: String, colorCode: Int) {
clearPendingTextPlacement()
val textEditorDialogFragment =
TextEditorDialogFragment.show(this, text.toString(), colorCode)
textEditorDialogFragment.setOnTextEditorListener(object :
Expand Down Expand Up @@ -383,11 +397,13 @@ class EditImageActivity : BaseActivity(), OnPhotoEditorListener, View.OnClickLis
}

override fun onEmojiClick(emojiUnicode: String) {
clearPendingTextPlacement()
mPhotoEditor.addEmoji(emojiUnicode)
mTxtCurrentTool.setText(R.string.label_emoji)
}

override fun onStickerClick(bitmap: Bitmap) {
clearPendingTextPlacement()
mPhotoEditor.addImage(bitmap)
mTxtCurrentTool.setText(R.string.label_sticker)
}
Expand All @@ -414,6 +430,9 @@ class EditImageActivity : BaseActivity(), OnPhotoEditorListener, View.OnClickLis
}

override fun onToolSelected(toolType: ToolType) {
if (toolType != ToolType.TEXT) {
clearPendingTextPlacement()
}
when (toolType) {
ToolType.SHAPE -> {
mPhotoEditor.setBrushDrawingMode(true)
Expand All @@ -424,16 +443,10 @@ class EditImageActivity : BaseActivity(), OnPhotoEditorListener, View.OnClickLis
}

ToolType.TEXT -> {
val textEditorDialogFragment = TextEditorDialogFragment.show(this)
textEditorDialogFragment.setOnTextEditorListener(object :
TextEditorDialogFragment.TextEditorListener {
override fun onDone(inputText: String, colorCode: Int) {
val styleBuilder = TextStyleBuilder()
styleBuilder.withTextColor(colorCode)
mPhotoEditor.addText(inputText, styleBuilder)
mTxtCurrentTool.setText(R.string.label_text)
}
})
mIsTextPlacementPending = true
mTextPlacementOverlay.visibility = View.VISIBLE
mTxtCurrentTool.setText(R.string.label_text)
showSnackbar(getString(R.string.msg_tap_image_for_text))
}

ToolType.ERASER -> {
Expand Down Expand Up @@ -491,7 +504,10 @@ class EditImageActivity : BaseActivity(), OnPhotoEditorListener, View.OnClickLis
}

override fun onBackPressed() {
if (mIsFilterVisible) {
if (mIsTextPlacementPending) {
clearPendingTextPlacement()
mTxtCurrentTool.setText(R.string.app_name)
} else if (mIsFilterVisible) {
showFilter(false)
mTxtCurrentTool.setText(R.string.app_name)
} else if (!mPhotoEditor.isCacheEmpty) {
Expand All @@ -501,6 +517,24 @@ class EditImageActivity : BaseActivity(), OnPhotoEditorListener, View.OnClickLis
}
}

private fun showTextEditorDialog(position: Position) {
val textEditorDialogFragment = TextEditorDialogFragment.show(this)
textEditorDialogFragment.setOnTextEditorListener(object :
TextEditorDialogFragment.TextEditorListener {
override fun onDone(inputText: String, colorCode: Int) {
val styleBuilder = TextStyleBuilder()
styleBuilder.withTextColor(colorCode)
mPhotoEditor.addText(inputText, styleBuilder, position)
mTxtCurrentTool.setText(R.string.label_text)
}
})
}

private fun clearPendingTextPlacement() {
mIsTextPlacementPending = false
mTextPlacementOverlay.visibility = View.GONE
}

companion object {

private const val TAG = "EditImageActivity"
Expand All @@ -511,4 +545,4 @@ class EditImageActivity : BaseActivity(), OnPhotoEditorListener, View.OnClickLis
const val ACTION_NEXTGEN_EDIT = "action_nextgen_edit"
const val PINCH_TEXT_SCALABLE_INTENT_KEY = "PINCH_TEXT_SCALABLE"
}
}
}
15 changes: 14 additions & 1 deletion app/src/main/res/layout/activity_edit_image.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@
app:layout_constraintTop_toTopOf="parent"
app:photo_src="@drawable/blank_image" />

<View
android:id="@+id/viewTextPlacementOverlay"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@android:color/transparent"
android:clickable="true"
android:focusable="true"
android:visibility="gone"
Comment on lines +33 to +37
app:layout_constraintBottom_toBottomOf="@+id/photoEditorView"
app:layout_constraintEnd_toEndOf="@+id/photoEditorView"
app:layout_constraintStart_toStartOf="@+id/photoEditorView"
app:layout_constraintTop_toTopOf="@+id/photoEditorView" />

<ImageView
android:id="@+id/imgUndo"
android:layout_width="@dimen/top_tool_icon_width"
Expand Down Expand Up @@ -139,4 +152,4 @@
app:layout_constraintEnd_toStartOf="@+id/imgUndo"
app:layout_constraintStart_toEndOf="@+id/imgCamera" />

</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
1 change: 1 addition & 0 deletions app/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<string name="label_text">Texte</string>
<string name="label_filter">Filtres</string>
<string name="label_adjust">Ajuster</string>
<string name="msg_tap_image_for_text">Touchez l\'image pour placer votre texte</string>
<string name="msg_save_image">Souhaitez-vous quitter sans enregistrer l\'image ?</string>
<string name="app_intent_name">PhotoEditor</string>
<string name="msg_save_image_to_share">Veuillez enregistrer l\'image pour la partager</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-pl/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<string name="label_text">Tekst</string>
<string name="label_filter">Filtr</string>
<string name="label_adjust">Dostosuj</string>
<string name="msg_tap_image_for_text">Dotknij obrazu, aby umieścić tekst</string>
<string name="msg_save_image">Czy na pewno chcesz wyjść bez zapisywania?</string>
<string name="app_intent_name">PhotoEditor</string>
<string name="msg_save_image_to_share">Zapisz obraz, aby udostępnić</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<string name="label_text">Text</string>
<string name="label_filter">Filter</string>
<string name="label_adjust">Adjust</string>
<string name="msg_tap_image_for_text">Tap the image to place your text</string>
<string name="msg_save_image">Are you want to exit without saving image ?</string>
<string name="app_intent_name">PhotoEditor</string>
<string name="msg_save_image_to_share">Please save image to share</string>
Expand Down
2 changes: 1 addition & 1 deletion photoeditor/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ dependencies {

ext {
PUBLISH_GROUP_ID = 'com.burhanrashid52'
PUBLISH_VERSION = '3.1.0'
PUBLISH_VERSION = '3.1.1'
PUBLISH_ARTIFACT_ID = 'photoeditor'
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,19 @@ internal class GraphicManager(
val redoStackCount
get() = mViewState.redoViewsCount

fun addView(graphic: Graphic) {
fun addView(graphic: Graphic, position: Position? = null) {
val view = graphic.rootView
val params = RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
)
params.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE)
if (position == null) {
params.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE)
} else {
params.addRule(RelativeLayout.ALIGN_PARENT_START, RelativeLayout.TRUE)
params.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE)
params.leftMargin = position.x
params.topMargin = position.y
}
mPhotoEditorView.addView(view, params)
mViewState.addAddedView(view)

Expand Down Expand Up @@ -99,4 +106,4 @@ internal class GraphicManager(

return redoStackCount > 0
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@ interface PhotoEditor {
@SuppressLint("ClickableViewAccessibility")
fun addText(text: String, colorCodeTextView: Int)

/**
* This adds the text on the [PhotoEditorView] at the provided initial [position].
* by default [TextView.setText] will be 18sp
*
* @param text text to display
* @param colorCodeTextView text color to be displayed
* @param position initial position in pixels from the top-left of the editor
*/
@SuppressLint("ClickableViewAccessibility")
fun addText(text: String, colorCodeTextView: Int, position: Position) {
addText(text, colorCodeTextView)
}
Comment on lines +39 to +53

/**
* This add the text on the [PhotoEditorView] with provided parameters
* by default [TextView.setText] will be 18sp
Expand All @@ -47,6 +60,20 @@ interface PhotoEditor {
@SuppressLint("ClickableViewAccessibility")
fun addText(textTypeface: Typeface?, text: String, colorCodeTextView: Int)

/**
* This adds the text on the [PhotoEditorView] at the provided initial [position].
* by default [TextView.setText] will be 18sp
*
* @param textTypeface typeface for custom font in the text
* @param text text to display
* @param colorCodeTextView text color to be displayed
* @param position initial position in pixels from the top-left of the editor
*/
@SuppressLint("ClickableViewAccessibility")
fun addText(textTypeface: Typeface?, text: String, colorCodeTextView: Int, position: Position) {
addText(textTypeface, text, colorCodeTextView)
}
Comment on lines +66 to +81

/**
* This add the text on the [PhotoEditorView] with provided parameters
* by default [TextView.setText] will be 18sp
Expand All @@ -57,6 +84,19 @@ interface PhotoEditor {
@SuppressLint("ClickableViewAccessibility")
fun addText(text: String, styleBuilder: TextStyleBuilder?)

/**
* This adds the text on the [PhotoEditorView] at the provided initial [position].
* by default [TextView.setText] will be 18sp
*
* @param text text to display
* @param styleBuilder text style builder with your style
* @param position initial position in pixels from the top-left of the editor
*/
@SuppressLint("ClickableViewAccessibility")
fun addText(text: String, styleBuilder: TextStyleBuilder?, position: Position) {
addText(text, styleBuilder)
}
Comment on lines +93 to +107

/**
* This will update text and color on provided view
*
Expand Down Expand Up @@ -383,4 +423,4 @@ interface PhotoEditor {
* through the use of a ShapeBuilder.
*/
fun setShape(shapeBuilder: ShapeBuilder) // endregion
}
}
Loading
Loading