Merge pull request #353 from KryptKode/feat/edit-metadata

Implement editing audio tags for specific media files
This commit is contained in:
Tibor Kaputa 2021-11-16 13:42:27 +01:00 committed by GitHub
commit ab53bb009f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 319 additions and 81 deletions

View file

@ -70,6 +70,9 @@ dependencies {
implementation 'com.google.android.material:material:1.4.0'
implementation 'com.airbnb.android:lottie:3.6.1'
// higher versions of jaudiotagger not compatible with <= API 25 devices
// https://bitbucket.org/ijabz/jaudiotagger/issues/149/some-nio-classes-are-unavailable-while
implementation "net.jthink:jaudiotagger:2.2.5"
kapt "androidx.room:room-compiler:2.3.0"
implementation "androidx.room:room-runtime:2.3.0"
annotationProcessor "androidx.room:room-compiler:2.3.0"

View file

@ -359,6 +359,11 @@ class MainActivity : SimpleActivity() {
playlists_fragment_holder?.setupFragment(this)
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun tracksUpdated(event: Events.RefreshTracks) {
tracks_fragment_holder?.setupFragment(this)
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun trackDeleted(event: Events.TrackDeleted) {
getAllFragments().forEach {

View file

@ -1,5 +1,6 @@
package com.simplemobiletools.musicplayer.adapters
import android.content.Intent
import android.view.Menu
import android.view.View
import android.view.ViewGroup
@ -18,18 +19,18 @@ import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.commons.views.MyRecyclerView
import com.simplemobiletools.musicplayer.R
import com.simplemobiletools.musicplayer.activities.SimpleActivity
import com.simplemobiletools.musicplayer.extensions.addTracksToPlaylist
import com.simplemobiletools.musicplayer.extensions.addTracksToQueue
import com.simplemobiletools.musicplayer.extensions.deleteTracks
import com.simplemobiletools.musicplayer.extensions.getAlbumTracksSync
import com.simplemobiletools.musicplayer.models.Album
import com.simplemobiletools.musicplayer.models.AlbumSection
import com.simplemobiletools.musicplayer.models.ListItem
import com.simplemobiletools.musicplayer.models.Track
import com.simplemobiletools.musicplayer.dialogs.EditDialog
import com.simplemobiletools.musicplayer.extensions.*
import com.simplemobiletools.musicplayer.helpers.EDIT
import com.simplemobiletools.musicplayer.helpers.EDITED_TRACK
import com.simplemobiletools.musicplayer.helpers.REFRESH_LIST
import com.simplemobiletools.musicplayer.models.*
import com.simplemobiletools.musicplayer.services.MusicService
import kotlinx.android.synthetic.main.item_album.view.*
import kotlinx.android.synthetic.main.item_section.view.*
import kotlinx.android.synthetic.main.item_track.view.*
import java.util.*
import org.greenrobot.eventbus.EventBus
// we show both albums and individual tracks here
class AlbumsTracksAdapter(
@ -95,6 +96,7 @@ class AlbumsTracksAdapter(
R.id.cab_add_to_playlist -> addToPlaylist()
R.id.cab_add_to_queue -> addToQueue()
R.id.cab_delete -> askConfirmDelete()
R.id.cab_rename -> displayEditDialog()
R.id.cab_select_all -> selectAll()
}
}
@ -235,4 +237,26 @@ class AlbumsTracksAdapter(
else -> ""
}
}
private fun displayEditDialog() {
getSelectedTracks().firstOrNull()?.let { selectedTrack ->
EditDialog(activity as SimpleActivity, selectedTrack) { track ->
val trackIndex = items.indexOfFirst { (it as? Track)?.mediaStoreId == track.mediaStoreId }
if (trackIndex != -1) {
items[trackIndex] = track
notifyItemChanged(trackIndex)
finishActMode()
}
if (track.mediaStoreId == MusicService.mCurrTrack?.mediaStoreId) {
Intent(activity, MusicService::class.java).apply {
putExtra(EDITED_TRACK, track)
action = EDIT
activity.startService(this)
}
}
activity.sendIntent(REFRESH_LIST)
EventBus.getDefault().post(Events.RefreshTracks())
}
}
}
}

View file

@ -1,6 +1,7 @@
package com.simplemobiletools.musicplayer.adapters
import android.content.ContentUris
import android.content.Intent
import android.provider.MediaStore
import android.view.Menu
import android.view.View
@ -18,12 +19,15 @@ import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.commons.views.MyRecyclerView
import com.simplemobiletools.musicplayer.R
import com.simplemobiletools.musicplayer.activities.SimpleActivity
import com.simplemobiletools.musicplayer.extensions.addTracksToPlaylist
import com.simplemobiletools.musicplayer.extensions.addTracksToQueue
import com.simplemobiletools.musicplayer.extensions.deleteTracks
import com.simplemobiletools.musicplayer.extensions.tracksDAO
import com.simplemobiletools.musicplayer.dialogs.EditDialog
import com.simplemobiletools.musicplayer.extensions.*
import com.simplemobiletools.musicplayer.helpers.EDIT
import com.simplemobiletools.musicplayer.helpers.EDITED_TRACK
import com.simplemobiletools.musicplayer.helpers.REFRESH_LIST
import com.simplemobiletools.musicplayer.helpers.TagHelper
import com.simplemobiletools.musicplayer.models.Events
import com.simplemobiletools.musicplayer.models.Track
import com.simplemobiletools.musicplayer.services.MusicService
import kotlinx.android.synthetic.main.item_track.view.*
import org.greenrobot.eventbus.EventBus
import java.util.*
@ -32,6 +36,7 @@ class TracksAdapter(
activity: SimpleActivity, var tracks: ArrayList<Track>, val isPlaylistContent: Boolean, recyclerView: MyRecyclerView, itemClick: (Any) -> Unit
) : MyRecyclerViewAdapter(activity, recyclerView, null, itemClick), RecyclerViewFastScroller.OnPopupTextUpdate {
private val tagHelper by lazy { TagHelper(activity) }
private var textToHighlight = ""
private val placeholder = resources.getColoredDrawableWithColor(R.drawable.ic_headset, textColor)
private val cornerRadius = resources.getDimension(R.dimen.rounded_corner_radius_small).toInt()
@ -57,6 +62,8 @@ class TracksAdapter(
override fun prepareActionMode(menu: Menu) {
menu.apply {
findItem(R.id.cab_remove_from_playlist).isVisible = isPlaylistContent
findItem(R.id.cab_rename).isVisible =
isOneItemSelected() && getSelectedTracks().firstOrNull()?.let { !it.path.startsWith("content://") && tagHelper.isEditTagSupported(it) } == true
}
}
@ -69,6 +76,7 @@ class TracksAdapter(
R.id.cab_add_to_playlist -> addToPlaylist()
R.id.cab_add_to_queue -> addToQueue()
R.id.cab_properties -> showProperties()
R.id.cab_rename -> displayEditDialog()
R.id.cab_remove_from_playlist -> removeFromPlaylist()
R.id.cab_delete -> askConfirmDelete()
R.id.cab_select_all -> selectAll()
@ -169,7 +177,7 @@ class TracksAdapter(
}
}
private fun getSelectedTracks(): List<Track> = tracks.filter { selectedKeys.contains(it.hashCode()) }.toList()
private fun getSelectedTracks(): List<Track> = tracks.filter { selectedKeys.contains(it.hashCode()) }
fun updateItems(newItems: ArrayList<Track>, highlightText: String = "", forceUpdate: Boolean = false) {
if (forceUpdate || newItems.hashCode() != tracks.hashCode()) {
@ -209,4 +217,27 @@ class TracksAdapter(
}
override fun onChange(position: Int) = tracks.getOrNull(position)?.getBubbleText() ?: ""
private fun displayEditDialog() {
getSelectedTracks().firstOrNull()?.let { selectedTrack ->
EditDialog(activity as SimpleActivity, selectedTrack) { track ->
val trackIndex = tracks.indexOfFirst { it.mediaStoreId == track.mediaStoreId }
tracks[trackIndex] = track
if (trackIndex != -1) {
tracks[trackIndex] = track
notifyItemChanged(trackIndex)
finishActMode()
}
if (track.mediaStoreId == MusicService.mCurrTrack?.mediaStoreId) {
Intent(activity, MusicService::class.java).apply {
putExtra(EDITED_TRACK, track)
action = EDIT
activity.startService(this)
}
}
activity.sendIntent(REFRESH_LIST)
EventBus.getDefault().post(Events.RefreshTracks())
}
}
}
}

View file

@ -1,5 +1,6 @@
package com.simplemobiletools.musicplayer.adapters
import android.content.Intent
import android.view.Menu
import android.view.View
import android.view.ViewGroup
@ -18,15 +19,24 @@ import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.commons.views.MyRecyclerView
import com.simplemobiletools.musicplayer.R
import com.simplemobiletools.musicplayer.activities.SimpleActivity
import com.simplemobiletools.musicplayer.dialogs.EditDialog
import com.simplemobiletools.musicplayer.extensions.addTracksToPlaylist
import com.simplemobiletools.musicplayer.extensions.addTracksToQueue
import com.simplemobiletools.musicplayer.extensions.deleteTracks
import com.simplemobiletools.musicplayer.extensions.sendIntent
import com.simplemobiletools.musicplayer.helpers.EDIT
import com.simplemobiletools.musicplayer.helpers.EDITED_TRACK
import com.simplemobiletools.musicplayer.helpers.REFRESH_LIST
import com.simplemobiletools.musicplayer.helpers.TagHelper
import com.simplemobiletools.musicplayer.models.AlbumHeader
import com.simplemobiletools.musicplayer.models.Events
import com.simplemobiletools.musicplayer.models.ListItem
import com.simplemobiletools.musicplayer.models.Track
import com.simplemobiletools.musicplayer.services.MusicService
import kotlinx.android.synthetic.main.item_album_header.view.*
import kotlinx.android.synthetic.main.item_track.view.*
import java.util.*
import org.greenrobot.eventbus.EventBus
class TracksHeaderAdapter(activity: SimpleActivity, val items: ArrayList<ListItem>, recyclerView: MyRecyclerView, itemClick: (Any) -> Unit) :
MyRecyclerViewAdapter(activity, recyclerView, null, itemClick), RecyclerViewFastScroller.OnPopupTextUpdate {
@ -36,6 +46,7 @@ class TracksHeaderAdapter(activity: SimpleActivity, val items: ArrayList<ListIte
private val placeholder = resources.getColoredDrawableWithColor(R.drawable.ic_headset, textColor)
private val cornerRadius = resources.getDimension(R.dimen.rounded_corner_radius_big).toInt()
private val tagHelper by lazy { TagHelper(activity) }
init {
setupDragListener(true)
@ -73,7 +84,13 @@ class TracksHeaderAdapter(activity: SimpleActivity, val items: ArrayList<ListIte
}
}
override fun prepareActionMode(menu: Menu) {}
override fun prepareActionMode(menu: Menu) {
menu.apply {
val oneItemsSelected = isOneItemSelected()
val selected = getSelectedTracks().firstOrNull()?.let { !it.path.startsWith("content://") && tagHelper.isEditTagSupported(it) } == true
findItem(R.id.cab_rename).isVisible = oneItemsSelected && selected
}
}
override fun actionItemPressed(id: Int) {
if (selectedKeys.isEmpty()) {
@ -84,6 +101,7 @@ class TracksHeaderAdapter(activity: SimpleActivity, val items: ArrayList<ListIte
R.id.cab_add_to_playlist -> addToPlaylist()
R.id.cab_add_to_queue -> addToQueue()
R.id.cab_delete -> askConfirmDelete()
R.id.cab_rename -> displayEditDialog()
R.id.cab_select_all -> selectAll()
}
}
@ -192,4 +210,26 @@ class TracksHeaderAdapter(activity: SimpleActivity, val items: ArrayList<ListIte
else -> ""
}
}
private fun displayEditDialog() {
getSelectedTracks().firstOrNull()?.let { selectedTrack ->
EditDialog(activity as SimpleActivity, selectedTrack) { track ->
val trackIndex = items.indexOfFirst { (it as? Track)?.mediaStoreId == track.mediaStoreId }
if (trackIndex != -1) {
items[trackIndex] = track
notifyItemChanged(trackIndex)
finishActMode()
}
if (track.mediaStoreId == MusicService.mCurrTrack?.mediaStoreId) {
Intent(activity, MusicService::class.java).apply {
putExtra(EDITED_TRACK, track)
action = EDIT
activity.startService(this)
}
}
activity.sendIntent(REFRESH_LIST)
EventBus.getDefault().post(Events.RefreshTracks())
}
}
}
}

View file

@ -1,93 +1,110 @@
package com.simplemobiletools.musicplayer.dialogs
import android.content.ContentValues
import android.content.Context
import android.provider.MediaStore.Audio
import androidx.appcompat.app.AlertDialog
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.commons.helpers.isRPlus
import com.simplemobiletools.musicplayer.R
import com.simplemobiletools.musicplayer.extensions.tracksDAO
import com.simplemobiletools.musicplayer.helpers.TagHelper
import com.simplemobiletools.musicplayer.models.Track
import kotlinx.android.synthetic.main.dialog_rename_song.*
import kotlinx.android.synthetic.main.dialog_rename_song.view.*
class EditDialog(val activity: BaseSimpleActivity, val song: Track, val callback: (Track) -> Unit) {
class EditDialog(val activity: BaseSimpleActivity, val track: Track, val callback: (Track) -> Unit) {
private val tagHelper by lazy { TagHelper(activity) }
init {
val view = activity.layoutInflater.inflate(R.layout.dialog_rename_song, null).apply {
song_title.setText(song.title)
song_artist.setText(song.artist)
val filename = song.path.getFilenameFromPath()
song_title.setText(track.title)
song_artist.setText(track.artist)
song_album.setText(track.album)
val filename = track.path.getFilenameFromPath()
file_name.setText(filename.substring(0, filename.lastIndexOf(".")))
extension.setText(song.path.getFilenameExtension())
extension.setText(track.path.getFilenameExtension())
if (isRPlus()) {
arrayOf(file_name_label, file_name, extension_label, extension).forEach { it.beGone() }
}
}
AlertDialog.Builder(activity)
.setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.cancel, null)
.create().apply {
activity.setupDialogStuff(view, this, R.string.rename_song) {
showKeyboard(song_title)
getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
val newTitle = view.song_title.value
val newArtist = view.song_artist.value
val newFilename = view.file_name.value
val newFileExtension = view.extension.value
.setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.cancel, null)
.create().apply {
activity.setupDialogStuff(view, this, R.string.rename_song) {
showKeyboard(song_title)
getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
val newTitle = view.song_title.value
val newArtist = view.song_artist.value
val newAlbum = view.song_album.value
val newFilename = view.file_name.value
val newFileExtension = view.extension.value
if (newTitle.isEmpty() || newArtist.isEmpty() || newFilename.isEmpty() || newFileExtension.isEmpty()) {
activity.toast(R.string.rename_song_empty)
return@setOnClickListener
}
if (newTitle.isEmpty() || newArtist.isEmpty() || newFilename.isEmpty() || newFileExtension.isEmpty()) {
activity.toast(R.string.rename_song_empty)
return@setOnClickListener
}
song.artist = newArtist
song.title = newTitle
updateContentResolver(context, song.mediaStoreId, newTitle, newArtist)
val oldPath = song.path
val newPath = "${oldPath.getParentPath()}/$newFilename.$newFileExtension"
if (oldPath == newPath) {
storeEditedSong(song, oldPath, newPath)
callback(song)
dismiss()
return@setOnClickListener
}
activity.renameFile(oldPath, newPath) {
if (it) {
storeEditedSong(song, oldPath, newPath)
song.path = newPath
callback(song)
} else {
activity.toast(R.string.rename_song_error)
if (track.title != newTitle || track.artist != newArtist || track.album != newAlbum) {
updateContentResolver(track, newArtist, newTitle, newAlbum) {
track.artist = newArtist
track.title = newTitle
track.album = newAlbum
val oldPath = track.path
val newPath = "${oldPath.getParentPath()}/$newFilename.$newFileExtension"
if (oldPath == newPath) {
storeEditedSong(track, oldPath, newPath)
callback(track)
dismiss()
return@updateContentResolver
}
if (!isRPlus()) {
activity.renameFile(oldPath, newPath) {
if (it) {
storeEditedSong(track, oldPath, newPath)
track.path = newPath
callback(track)
} else {
activity.toast(R.string.rename_song_error)
}
dismiss()
}
}
dismiss()
}
} else {
dismiss()
}
}
}
}
}
private fun storeEditedSong(song: Track, oldPath: String, newPath: String) {
private fun storeEditedSong(track: Track, oldPath: String, newPath: String) {
ensureBackgroundThread {
try {
activity.tracksDAO.updateSongInfo(newPath, song.artist, song.title, oldPath)
activity.tracksDAO.updateSongInfo(newPath, track.artist, track.title, oldPath)
} catch (e: Exception) {
activity.showErrorToast(e)
}
}
}
private fun updateContentResolver(context: Context, songID: Long, newSongTitle: String, newSongArtist: String) {
val uri = Audio.Media.EXTERNAL_CONTENT_URI
val where = "${Audio.Media._ID} = ?"
val args = arrayOf(songID.toString())
val values = ContentValues().apply {
put(Audio.Media.TITLE, newSongTitle)
put(Audio.Media.ARTIST, newSongArtist)
private fun updateContentResolver(track: Track, newArtist: String, newTitle: String, newAlbum: String, onUpdateMediaStore: () -> Unit) {
ensureBackgroundThread {
try {
activity.handleRecoverableSecurityException { granted ->
if (granted) {
tagHelper.writeTag(track, newArtist, newTitle, newAlbum)
activity.runOnUiThread {
onUpdateMediaStore.invoke()
}
}
}
} catch (e: Exception) {
activity.toast(R.string.unknown_error_occurred)
}
}
context.contentResolver.update(uri, values, where, args)
}
}

View file

@ -0,0 +1,86 @@
package com.simplemobiletools.musicplayer.helpers
import android.content.ContentUris
import android.content.ContentValues
import android.provider.MediaStore
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.getFilenameExtension
import com.simplemobiletools.commons.extensions.getFilenameFromPath
import com.simplemobiletools.commons.extensions.getTempFile
import com.simplemobiletools.musicplayer.models.Track
import org.jaudiotagger.audio.AudioFileIO
import org.jaudiotagger.audio.SupportedFileFormat
import org.jaudiotagger.tag.FieldKey
import org.jaudiotagger.tag.Tag
import org.jaudiotagger.tag.TagOptionSingleton
import org.jaudiotagger.tag.flac.FlacTag
import org.jaudiotagger.tag.id3.ID3v24Tag
import org.jaudiotagger.tag.mp4.Mp4Tag
import org.jaudiotagger.tag.vorbiscomment.VorbisCommentTag
class TagHelper(private val activity: BaseSimpleActivity) {
init {
TagOptionSingleton.getInstance().isAndroid = true
}
companion object {
private const val TEMP_FOLDER = "music"
// Editing tags in WMA and WAV files are flaky so we exclude them
private val EXCLUDED_EXTENSIONS = listOf("wma", "wav")
private val SUPPORTED_EXTENSIONS = SupportedFileFormat.values().map { it.filesuffix }.filter { it !in EXCLUDED_EXTENSIONS }
}
fun isEditTagSupported(track: Track): Boolean {
return SUPPORTED_EXTENSIONS.any { it == track.path.getFilenameExtension() }
}
fun writeTag(track: Track, newArtist: String, newTitle: String, newAlbum: String) {
if (isEditTagSupported(track)) {
val uri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, track.mediaStoreId)
val temp = activity.getTempFile(TEMP_FOLDER, track.path.getFilenameFromPath())
activity.contentResolver.openInputStream(uri)!!.use { inputStream ->
temp!!.outputStream().use { out ->
inputStream.copyTo(out)
}
}
val audioFile = AudioFileIO.read(temp)
val tag = audioFile.tag ?: createTag(track.path.getFilenameExtension()).also { audioFile.tag = it }
tag.setField(FieldKey.TITLE, newTitle)
tag.setField(FieldKey.ARTIST, newArtist)
tag.setField(FieldKey.ALBUM, newAlbum)
audioFile.commit()
activity.contentResolver.openOutputStream(uri, "w")!!.use { outputStream ->
outputStream.write(temp!!.readBytes())
}
temp!!.delete()
updateContentResolver(track)
}
}
private fun createTag(extension: String): Tag {
return when (extension) {
SupportedFileFormat.OGG.filesuffix -> VorbisCommentTag()
SupportedFileFormat.M4A.filesuffix -> Mp4Tag()
SupportedFileFormat.FLAC.filesuffix -> FlacTag()
else -> ID3v24Tag()
}
}
private fun updateContentResolver(track: Track) {
val uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val where = "${MediaStore.Audio.Media._ID} = ?"
val args = arrayOf(track.mediaStoreId.toString())
val values = ContentValues().apply {
put(MediaStore.Audio.Media.TITLE, track.title)
put(MediaStore.Audio.Media.ARTIST, track.artist)
put(MediaStore.Audio.Media.ALBUM, track.album)
}
activity.contentResolver.update(uri, values, where, args)
}
}

View file

@ -12,4 +12,5 @@ class Events {
class PlaylistsUpdated
class TrackDeleted
class NoStoragePermission
class RefreshTracks
}

View file

@ -23,7 +23,7 @@ data class Track(
@ColumnInfo(name = "artist") var artist: String,
@ColumnInfo(name = "path") var path: String,
@ColumnInfo(name = "duration") var duration: Int,
@ColumnInfo(name = "album") val album: String,
@ColumnInfo(name = "album") var album: String,
@ColumnInfo(name = "cover_art") val coverArt: String,
@ColumnInfo(name = "playlist_id") var playListId: Int,
@ColumnInfo(name = "track_id") val trackId: Int // order id within the tracks' album

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rename_song_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -16,7 +15,7 @@
android:id="@+id/song_title_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title"/>
android:text="@string/title" />
<com.simplemobiletools.commons.views.MyEditText
android:id="@+id/song_title"
@ -26,13 +25,13 @@
android:inputType="textCapWords"
android:singleLine="true"
android:textCursorDrawable="@null"
android:textSize="@dimen/normal_text_size"/>
android:textSize="@dimen/normal_text_size" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/song_artist_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/artist"/>
android:text="@string/artist" />
<com.simplemobiletools.commons.views.MyEditText
android:id="@+id/song_artist"
@ -42,13 +41,29 @@
android:inputType="textCapWords"
android:singleLine="true"
android:textCursorDrawable="@null"
android:textSize="@dimen/normal_text_size"/>
android:textSize="@dimen/normal_text_size" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/song_album_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/album" />
<com.simplemobiletools.commons.views.MyEditText
android:id="@+id/song_album"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/activity_margin"
android:inputType="textCapWords"
android:singleLine="true"
android:textCursorDrawable="@null"
android:textSize="@dimen/normal_text_size" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/file_name_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/filename"/>
android:text="@string/filename" />
<com.simplemobiletools.commons.views.MyEditText
android:id="@+id/file_name"
@ -57,13 +72,13 @@
android:layout_marginBottom="@dimen/activity_margin"
android:singleLine="true"
android:textCursorDrawable="@null"
android:textSize="@dimen/normal_text_size"/>
android:textSize="@dimen/normal_text_size" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/extension_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/extension"/>
android:text="@string/extension" />
<com.simplemobiletools.commons.views.MyEditText
android:id="@+id/extension"
@ -72,7 +87,7 @@
android:layout_marginBottom="@dimen/activity_margin"
android:singleLine="true"
android:textCursorDrawable="@null"
android:textSize="@dimen/normal_text_size"/>
android:textSize="@dimen/normal_text_size" />
</LinearLayout>
</ScrollView>

View file

@ -6,6 +6,11 @@
android:icon="@drawable/ic_delete_vector"
android:title="@string/delete"
app:showAsAction="ifRoom" />
<item
android:id="@+id/cab_rename"
android:icon="@drawable/ic_rename_vector"
android:title="@string/rename"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/cab_add_to_playlist"
android:title="@string/add_to_playlist"

View file

@ -16,6 +16,11 @@
android:icon="@drawable/ic_info_vector"
android:title="@string/properties"
app:showAsAction="ifRoom" />
<item
android:id="@+id/cab_rename"
android:icon="@drawable/ic_rename_vector"
android:title="@string/rename"
app:showAsAction="ifRoom" />
<item
android:id="@+id/cab_add_to_playlist"
android:title="@string/add_to_playlist"

View file

@ -6,6 +6,11 @@
android:icon="@drawable/ic_delete_vector"
android:title="@string/delete"
app:showAsAction="ifRoom" />
<item
android:id="@+id/cab_rename"
android:icon="@drawable/ic_rename_vector"
android:title="@string/rename"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/cab_add_to_playlist"
android:title="@string/add_to_playlist"

View file

@ -120,4 +120,4 @@
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View file

@ -20,6 +20,7 @@ buildscript {
allprojects {
repositories {
google()
mavenCentral()
jcenter()
maven { url "https://jitpack.io" }
}