mirror of
https://github.com/cmclark00/RetroMusicPlayer.git
synced 2025-05-18 08:05:20 +01:00
SAF is fixed
This commit is contained in:
parent
8789eeb854
commit
570a235836
25 changed files with 907 additions and 142 deletions
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package code.name.monkey.retromusic.activities.saf;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.heinrichreimersoftware.materialintro.app.IntroActivity;
|
||||
import com.heinrichreimersoftware.materialintro.slide.SimpleSlide;
|
||||
|
||||
import code.name.monkey.retromusic.R;
|
||||
|
||||
/**
|
||||
* Created by hemanths on 2019-07-31.
|
||||
*/
|
||||
public class SAFGuideActivity extends IntroActivity {
|
||||
public static final int REQUEST_CODE_SAF_GUIDE = 98;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setButtonCtaVisible(false);
|
||||
setButtonNextVisible(false);
|
||||
setButtonBackVisible(false);
|
||||
|
||||
setButtonCtaTintMode(BUTTON_CTA_TINT_MODE_TEXT);
|
||||
|
||||
String title = String.format(getString(R.string.saf_guide_slide1_title), getString(R.string.app_name));
|
||||
|
||||
addSlide(new SimpleSlide.Builder()
|
||||
.title(title)
|
||||
.description(Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1 ? R.string.saf_guide_slide1_description_before_o : R.string.saf_guide_slide1_description)
|
||||
.image(R.drawable.saf_guide_1)
|
||||
.background(R.color.md_deep_purple_300)
|
||||
.backgroundDark(R.color.md_deep_purple_400)
|
||||
.layout(R.layout.fragment_simple_slide_large_image)
|
||||
.build());
|
||||
addSlide(new SimpleSlide.Builder()
|
||||
.title(R.string.saf_guide_slide2_title)
|
||||
.description(R.string.saf_guide_slide2_description)
|
||||
.image(R.drawable.saf_guide_2)
|
||||
.background(R.color.md_deep_purple_500)
|
||||
.backgroundDark(R.color.md_deep_purple_600)
|
||||
.layout(R.layout.fragment_simple_slide_large_image)
|
||||
.build());
|
||||
addSlide(new SimpleSlide.Builder()
|
||||
.title(R.string.saf_guide_slide3_title)
|
||||
.description(R.string.saf_guide_slide3_description)
|
||||
.image(R.drawable.saf_guide_3)
|
||||
.background(R.color.md_deep_purple_700)
|
||||
.backgroundDark(R.color.md_deep_purple_800)
|
||||
.layout(R.layout.fragment_simple_slide_large_image)
|
||||
.build());
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import android.content.res.ColorStateList
|
|||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.MenuItem
|
||||
|
@ -16,9 +17,10 @@ import code.name.monkey.appthemehelper.ThemeStore
|
|||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.activities.saf.SAFGuideActivity
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import code.name.monkey.retromusic.util.SAFUtil
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.list.listItems
|
||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
|
@ -27,6 +29,8 @@ import org.jaudiotagger.audio.AudioFile
|
|||
import org.jaudiotagger.audio.AudioFileIO
|
||||
import org.jaudiotagger.tag.FieldKey
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
|
||||
abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
||||
|
||||
|
@ -38,9 +42,14 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
|||
private var songPaths: List<String>? = null
|
||||
lateinit var saveFab: ExtendedFloatingActionButton
|
||||
|
||||
private var savedSongPaths: List<String>? = null
|
||||
private val currentSongPath: String? = null
|
||||
private var savedTags: Map<FieldKey, String>? = null
|
||||
private var savedArtworkInfo: ArtworkInfo? = null
|
||||
|
||||
protected val show: MaterialDialog
|
||||
get() = MaterialDialog(this@AbsTagEditorActivity).show {
|
||||
title(R.string.update_image)
|
||||
title(code.name.monkey.retromusic.R.string.update_image)
|
||||
listItems(items = items) { _, position, _ ->
|
||||
when (position) {
|
||||
0 -> getImageFromLastFM()
|
||||
|
@ -174,7 +183,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
|||
super.onCreate(savedInstanceState)
|
||||
setContentView(contentViewLayout)
|
||||
|
||||
saveFab = findViewById(R.id.saveTags)
|
||||
saveFab = findViewById(code.name.monkey.retromusic.R.id.saveTags)
|
||||
getIntentExtras()
|
||||
|
||||
songPaths = getSongPaths()
|
||||
|
@ -204,14 +213,14 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
|||
|
||||
private fun setUpImageView() {
|
||||
loadCurrentImage()
|
||||
items = listOf(getString(R.string.download_from_last_fm), getString(R.string.pick_from_local_storage), getString(R.string.web_search), getString(R.string.remove_cover))
|
||||
items = listOf(getString(code.name.monkey.retromusic.R.string.download_from_last_fm), getString(code.name.monkey.retromusic.R.string.pick_from_local_storage), getString(code.name.monkey.retromusic.R.string.web_search), getString(code.name.monkey.retromusic.R.string.remove_cover))
|
||||
editorImage.setOnClickListener { show }
|
||||
}
|
||||
|
||||
private fun startImagePicker() {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
intent.type = "image/*"
|
||||
startActivityForResult(Intent.createChooser(intent, getString(R.string.pick_from_local_storage)), REQUEST_CODE_SELECT_IMAGE)
|
||||
startActivityForResult(Intent.createChooser(intent, getString(code.name.monkey.retromusic.R.string.pick_from_local_storage)), REQUEST_CODE_SELECT_IMAGE)
|
||||
}
|
||||
|
||||
protected abstract fun loadCurrentImage()
|
||||
|
@ -295,9 +304,19 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
|||
saveFab.isEnabled = true
|
||||
}
|
||||
|
||||
private fun hideFab() {
|
||||
saveFab.animate()
|
||||
.setDuration(500)
|
||||
.setInterpolator(OvershootInterpolator())
|
||||
.scaleX(0.0f)
|
||||
.scaleY(0.0f)
|
||||
.start()
|
||||
saveFab.isEnabled = false
|
||||
}
|
||||
|
||||
protected fun setImageBitmap(bitmap: Bitmap?, bgColor: Int) {
|
||||
if (bitmap == null) {
|
||||
editorImage.setImageResource(R.drawable.default_album_art)
|
||||
editorImage.setImageResource(code.name.monkey.retromusic.R.drawable.default_album_art)
|
||||
} else {
|
||||
editorImage.setImageBitmap(bitmap)
|
||||
}
|
||||
|
@ -312,16 +331,50 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
|||
artworkInfo: ArtworkInfo?) {
|
||||
RetroUtil.hideSoftKeyboard(this)
|
||||
|
||||
WriteTagsAsyncTask(this)
|
||||
.execute(WriteTagsAsyncTask.LoadingInfo(getSongPaths(), fieldKeyValueMap, artworkInfo))
|
||||
hideFab()
|
||||
|
||||
savedSongPaths = getSongPaths()
|
||||
savedTags = fieldKeyValueMap
|
||||
savedArtworkInfo = artworkInfo
|
||||
|
||||
if (!SAFUtil.isSAFRequired(savedSongPaths)) {
|
||||
writeTags(savedSongPaths)
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
if (SAFUtil.isSDCardAccessGranted(this)) {
|
||||
writeTags(savedSongPaths)
|
||||
} else {
|
||||
startActivityForResult(Intent(this, SAFGuideActivity::class.java), SAFGuideActivity.REQUEST_CODE_SAF_GUIDE)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
private fun writeTags(paths: List<String>?) {
|
||||
WriteTagsAsyncTask(this).execute(WriteTagsAsyncTask.LoadingInfo(paths, savedTags, savedArtworkInfo))
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, intent)
|
||||
when (requestCode) {
|
||||
REQUEST_CODE_SELECT_IMAGE -> if (resultCode == Activity.RESULT_OK) {
|
||||
val selectedImage = data!!.data
|
||||
loadImageFromFile(selectedImage)
|
||||
intent?.data?.let {
|
||||
loadImageFromFile(it)
|
||||
}
|
||||
}
|
||||
SAFGuideActivity.REQUEST_CODE_SAF_GUIDE -> {
|
||||
SAFUtil.openTreePicker(this)
|
||||
}
|
||||
SAFUtil.REQUEST_SAF_PICK_TREE -> {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
SAFUtil.saveTreeUri(this, intent)
|
||||
writeTags(savedSongPaths)
|
||||
}
|
||||
}
|
||||
SAFUtil.REQUEST_SAF_PICK_FILE -> {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
writeTags(Collections.singletonList(currentSongPath + SAFUtil.SEPARATOR + intent!!.dataString))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -335,7 +388,6 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
|||
Log.e(TAG, "Could not read audio file $path", e)
|
||||
AudioFile()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ArtworkInfo constructor(val albumId: Int, val artwork: Bitmap?)
|
||||
|
|
|
@ -56,6 +56,7 @@ class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
|||
|
||||
songText.appHandleColor().addTextChangedListener(this)
|
||||
albumText.appHandleColor().addTextChangedListener(this)
|
||||
albumArtistText.appHandleColor().addTextChangedListener(this)
|
||||
artistText.appHandleColor().addTextChangedListener(this)
|
||||
genreText.appHandleColor().addTextChangedListener(this)
|
||||
yearText.appHandleColor().addTextChangedListener(this)
|
||||
|
|
|
@ -5,43 +5,43 @@ import android.app.Dialog;
|
|||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.afollestad.materialdialogs.bottomsheets.BottomSheet;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import org.jaudiotagger.audio.AudioFile;
|
||||
import org.jaudiotagger.audio.AudioFileIO;
|
||||
import org.jaudiotagger.audio.exceptions.CannotReadException;
|
||||
import org.jaudiotagger.audio.exceptions.CannotWriteException;
|
||||
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
|
||||
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
|
||||
import org.jaudiotagger.tag.FieldKey;
|
||||
import org.jaudiotagger.tag.Tag;
|
||||
import org.jaudiotagger.tag.TagException;
|
||||
import org.jaudiotagger.tag.images.Artwork;
|
||||
import org.jaudiotagger.tag.images.ArtworkFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.misc.DialogAsyncTask;
|
||||
import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener;
|
||||
import code.name.monkey.retromusic.util.MusicUtil;
|
||||
import code.name.monkey.retromusic.util.SAFUtil;
|
||||
|
||||
public class WriteTagsAsyncTask extends
|
||||
DialogAsyncTask<WriteTagsAsyncTask.LoadingInfo, Integer, String[]> {
|
||||
|
||||
private Context applicationContext;
|
||||
private WeakReference<Activity> activity;
|
||||
|
||||
public WriteTagsAsyncTask(Context context) {
|
||||
super(context);
|
||||
applicationContext = context;
|
||||
public WriteTagsAsyncTask(@NonNull Activity activity) {
|
||||
super(activity);
|
||||
this.activity = new WeakReference<>(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -68,6 +68,13 @@ public class WriteTagsAsyncTask extends
|
|||
for (String filePath : info.filePaths) {
|
||||
publishProgress(++counter, info.filePaths.size());
|
||||
try {
|
||||
Uri safUri = null;
|
||||
if (filePath.contains(SAFUtil.SEPARATOR)) {
|
||||
String[] fragments = filePath.split(SAFUtil.SEPARATOR);
|
||||
filePath = fragments[0];
|
||||
safUri = Uri.parse(fragments[1]);
|
||||
}
|
||||
|
||||
AudioFile audioFile = AudioFileIO.read(new File(filePath));
|
||||
Tag tag = audioFile.getTagOrCreateAndSetDefault();
|
||||
|
||||
|
@ -92,8 +99,10 @@ public class WriteTagsAsyncTask extends
|
|||
}
|
||||
}
|
||||
|
||||
audioFile.commit();
|
||||
} catch (@NonNull CannotReadException | IOException | CannotWriteException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
|
||||
Activity activity = this.activity.get();
|
||||
SAFUtil.write(activity, audioFile, safUri);
|
||||
|
||||
} catch (@NonNull Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +116,17 @@ public class WriteTagsAsyncTask extends
|
|||
}
|
||||
}
|
||||
|
||||
return info.filePaths.toArray(new String[info.filePaths.size()]);
|
||||
Collection<String> paths = info.filePaths;
|
||||
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
|
||||
paths = new ArrayList<>(info.filePaths.size());
|
||||
for (String path : info.filePaths) {
|
||||
if (path.contains(SAFUtil.SEPARATOR))
|
||||
path = path.split(SAFUtil.SEPARATOR)[0];
|
||||
paths.add(path);
|
||||
}
|
||||
}
|
||||
|
||||
return paths.toArray(new String[paths.size()]);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
|
@ -127,18 +146,20 @@ public class WriteTagsAsyncTask extends
|
|||
}
|
||||
|
||||
private void scan(String[] toBeScanned) {
|
||||
Context context = getContext();
|
||||
MediaScannerConnection.scanFile(applicationContext, toBeScanned, null,
|
||||
context instanceof Activity ? new UpdateToastMediaScannerCompletionListener(
|
||||
(Activity) context, toBeScanned) : null);
|
||||
Activity activity = this.activity.get();
|
||||
if (activity != null) {
|
||||
MediaScannerConnection.scanFile(activity, toBeScanned, null, new UpdateToastMediaScannerCompletionListener(activity, toBeScanned));
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Dialog createDialog(@NonNull Context context) {
|
||||
return new MaterialDialog(context, new BottomSheet())
|
||||
.title(R.string.saving_changes, "")
|
||||
.cancelable(false);
|
||||
return new MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.saving_changes)
|
||||
.setCancelable(false)
|
||||
.setView(R.layout.loading)
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue