package com.npes87184.s2tdroid.donate;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.support.annotation.NonNull;
import android.support.v4.provider.DocumentFile;
import android.support.v7.app.AlertDialog;
import android.support.v7.view.ContextThemeWrapper;
import android.widget.EditText;
import android.widget.Toast;
import com.github.angads25.filepicker.controller.DialogSelectionListener;
import com.github.angads25.filepicker.model.DialogConfigs;
import com.github.angads25.filepicker.model.DialogProperties;
import com.github.angads25.filepicker.view.FilePickerDialog;
import com.npes87184.s2tdroid.donate.model.Analysis;
import com.npes87184.s2tdroid.donate.model.FileUtil;
import com.npes87184.s2tdroid.donate.model.KeyCollection;
import org.mozilla.universalchardet.UniversalDetector;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.text.NumberFormat;
import java.util.ArrayList;
import cn.pedant.SweetAlert.SweetAlertDialog;
/**
* Created by npes87184 on 2015/5/17.
*/
public class HomeFragment extends PreferenceFragment implements
SharedPreferences.OnSharedPreferenceChangeListener {
private static final int REQUEST_CODE_STORAGE_ACCESS = 0;
int wordNumber = 0;
private Preference inputPreference;
private Preference outputPreference;
private Preference startPreference;
private SharedPreferences prefs;
private SweetAlertDialog pDialog;
private String [] filter = { "txt", "lrc", "trc", "srt", "ssa", "ass", "saa", "ini" };
private float progressNum = 0;
String booknameString = "S2TDroid";
Object syncToken = new Object();
public static HomeFragment newInstance() {
HomeFragment fragment = new HomeFragment();
return fragment;
}
private boolean contain(String[] strings, String s) {
for(int i=0;i< strings.length;++i) {
if(strings[i].equals(s)) {
return true;
}
}
return false;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
prefs = getPreferenceManager().getSharedPreferences();
prefs.registerOnSharedPreferenceChangeListener(this);
pDialog = new SweetAlertDialog(getActivity(), SweetAlertDialog.PROGRESS_TYPE)
.setTitleText(getString(R.string.wait));
inputPreference = findPreference(KeyCollection.KEY_INPUT_FILE);
inputPreference.setSummary(prefs.getString(KeyCollection.KEY_INPUT_FILE, DialogConfigs.DEFAULT_DIR));
inputPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
Toast.makeText(getActivity(), getString(R.string.choose_tip), Toast.LENGTH_LONG).show();
DialogProperties properties = new DialogProperties();
properties.selection_mode = DialogConfigs.SINGLE_MODE;
properties.selection_type = DialogConfigs.FILE_AND_DIR_SELECT;
properties.root = new File("/");
properties.error_dir = new File(DialogConfigs.DEFAULT_DIR);
properties.offset = new File(prefs.getString(KeyCollection.KEY_PATH, DialogConfigs.DEFAULT_DIR));
properties.extensions = filter;
FilePickerDialog dialog = new FilePickerDialog(getActivity(), properties);
dialog.setPositiveBtnName(getString(R.string.select));
dialog.setNegativeBtnName(getString(R.string.cancel));
dialog.setDialogSelectionListener(new DialogSelectionListener() {
@Override
public void onSelectedFilePaths(String[] files) {
inputPreference.getEditor().putString(KeyCollection.KEY_INPUT_FILE, files[0] + "/").commit();
prefs.edit().putString(KeyCollection.KEY_PATH, new File(files[0]).getParent()).apply();
}
});
dialog.show();
return true;
}
});
outputPreference = findPreference(KeyCollection.KEY_OUTPUT_FOLDER);
outputPreference.setSummary(prefs.getString(KeyCollection.KEY_OUTPUT_FOLDER, DialogConfigs.DEFAULT_DIR));
outputPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
DialogProperties properties = new DialogProperties();
properties.selection_mode = DialogConfigs.SINGLE_MODE;
properties.selection_type = DialogConfigs.DIR_SELECT;
properties.root = new File("/");
properties.error_dir = new File(DialogConfigs.DEFAULT_DIR);
properties.offset = new File(prefs.getString(KeyCollection.KEY_OUTPUT_FOLDER, DialogConfigs.DEFAULT_DIR));
FilePickerDialog dialog = new FilePickerDialog(getActivity(), properties);
dialog.setPositiveBtnName(getString(R.string.select));
dialog.setNegativeBtnName(getString(R.string.cancel));
dialog.setDialogSelectionListener(new DialogSelectionListener() {
@Override
public void onSelectedFilePaths(String[] files) {
outputPreference.getEditor().putString(KeyCollection.KEY_OUTPUT_FOLDER, files[0] + "/").commit();
}
});
dialog.show();
return true;
}
});
startPreference = findPreference(KeyCollection.KEY_START);
startPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(final Preference preference) {
pDialog.show();
pDialog.setCancelable(false);
final File testSDcardFolder = new File(prefs.getString(KeyCollection.KEY_OUTPUT_FOLDER, DialogConfigs.DEFAULT_DIR));
new Thread(new Runnable() {
@Override
public void run() {
Message msg;
if (checkFolder(testSDcardFolder)) {
try {
File testDirFile = new File(prefs.getString(KeyCollection.KEY_INPUT_FILE, DialogConfigs.DEFAULT_DIR));
if (!testDirFile.exists()) {
msg = new Message();
msg.what = 2;
mHandler.sendMessage(msg);
Thread.currentThread().interrupt();
return;
}
ArrayList<String> fileList = new ArrayList<String>();
prepareFileList(testDirFile, fileList);
for (int i = 0; i < fileList.size(); ++i) {
File inFile = new File(fileList.get(i));
// detect encode
String encodeString = detectEncode(fileList.get(i));
int totalLine = countLines(fileList.get(i), encodeString);
int TorS = 0; // >0 means t2s
if (encodeString.equals("GBK")) {
TorS = -100;
} else if (encodeString.equals("BIG5")) {
TorS = 100;
}
String file_extension = getFileExtension(inFile);
String name = getFileName(inFile);
InputStream is = new FileInputStream(inFile);
InputStreamReader isr = new InputStreamReader(is, encodeString);
BufferedReader bReader = new BufferedReader(isr);
String line;
if (prefs.getString(KeyCollection.KEY_MODE, "0").equals("0")) {
line = bReader.readLine();
if (Analysis.isTraditional(line) >= 0) {
booknameString = Analysis.TtoS(line);
} else {
booknameString = Analysis.StoT(line);
}
} else {
booknameString = prefs.getString(KeyCollection.KEY_MODE, "s2t").equals("s2t") ? Analysis.StoT(bReader.readLine()) : Analysis.TtoS(bReader.readLine());
}
String firstLine = booknameString;
// fix too large bookname
if (booknameString.length() > 15) {
booknameString = booknameString.substring(0, 15);
}
if (prefs.getBoolean(KeyCollection.KEY_SAME_FILENAME, false)) {
booknameString = name;
} else {
msg = new Message();
msg.what = 4;
mHandler.sendMessage(msg);
synchronized (syncToken) {
syncToken.wait();
}
booknameString = booknameString.split(" ")[0];
if (!isFilenameValid(booknameString)) {
Message filenameNotValidMsg = new Message();
filenameNotValidMsg.what = 6;
mHandler.sendMessage(filenameNotValidMsg);
Thread.currentThread().interrupt();
return;
}
}
File file = new File(prefs.getString(KeyCollection.KEY_OUTPUT_FOLDER, DialogConfigs.DEFAULT_DIR));
if (!file.exists() || !file.isDirectory()) {
file.mkdir();
}
File outFile = getOutFile(prefs.getString(KeyCollection.KEY_OUTPUT_FOLDER, DialogConfigs.DEFAULT_DIR), booknameString, file_extension);
OutputStreamWriter osw = getOutputStreamWriter(outFile);
// doing transform
BufferedWriter bw = new BufferedWriter(osw);
bw.write(firstLine + "\r");
bw.newLine();
while ((line = bReader.readLine()) != null) {
progressNum += ((1) / (float) fileList.size()) * (1 / (float) totalLine);
msg = new Message();
msg.what = 5;
mHandler.sendMessage(msg);
if (line.length() == 0) {
bw.write("\r");
bw.newLine();
continue;
}
wordNumber += line.length();
if (prefs.getString(KeyCollection.KEY_MODE, "0").equals("0")) {
if (TorS < 100 && TorS > -100) {
// detect step
TorS += Analysis.isTraditional(line);
if (TorS >= 0) {
bw.write(Analysis.TtoS(line) + "\r");
} else {
bw.write(Analysis.StoT(line) + "\r");
}
} else {
if (TorS > 0) {
bw.write(Analysis.TtoS(line) + "\r");
} else {
bw.write(Analysis.StoT(line) + "\r");
}
}
} else {
if (prefs.getString(KeyCollection.KEY_MODE, "s2t").equals("s2t")) {
bw.write(Analysis.StoT(line) + "\r");
} else {
bw.write(Analysis.TtoS(line) + "\r");
}
}
bw.newLine();
}
bw.close();
osw.close();
bReader.close();
isr.close();
is.close();
//media rescan for correctly showing in pc
MediaScannerConnection.scanFile(getActivity(), new String[]{outFile.getAbsolutePath()}, null, null);
if (prefs.getBoolean(KeyCollection.KEY_DELETE_SOURCE, false)) {
deleteSourceFile(inFile);
}
}
} catch (Exception e) {
}
msg = new Message();
msg.what = 1;
mHandler.sendMessage(msg);
} else {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
pDialog.hide();
// trigger SAF or in kikat.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
final SweetAlertDialog sdcardDialog = new SweetAlertDialog(getActivity(), SweetAlertDialog.ERROR_TYPE);
sdcardDialog.setTitleText(getString(R.string.oops))
.setContentText(getString(R.string.oops_sdcard_detail))
.setConfirmText("OK")
.setConfirmClickListener(new SweetAlertDialog.OnSweetClickListener() {
@Override
public void onClick(SweetAlertDialog sweetAlertDialog) {
triggerStorageAccessFramework();
sdcardDialog.dismiss();
}
})
.show();
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
final SweetAlertDialog sdcardDialog = new SweetAlertDialog(getActivity(), SweetAlertDialog.ERROR_TYPE);
sdcardDialog.setTitleText(getString(R.string.oops))
.setContentText(getString(R.string.oops_sdcard_kitkat_detail))
.setConfirmText("OK")
.setConfirmClickListener(new SweetAlertDialog.OnSweetClickListener() {
@Override
public void onClick(SweetAlertDialog sweetAlertDialog) {
sdcardDialog.dismiss();
}
})
.show();
}
}
});
}
}
}).start();
return true;
}
});
}
Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
pDialog.getProgressHelper().setInstantProgress(1);
pDialog.hide();
NumberFormat nf = NumberFormat.getInstance();
new SweetAlertDialog(getActivity(), SweetAlertDialog.SUCCESS_TYPE)
.setTitleText(getString(R.string.word_count) + nf.format(wordNumber))
.setConfirmText("OK")
.show();
wordNumber = 0;
progressNum = 0;
pDialog = new SweetAlertDialog(getActivity(), SweetAlertDialog.PROGRESS_TYPE)
.setTitleText(getString(R.string.wait));
break;
case 2:
pDialog.hide();
new SweetAlertDialog(getActivity(), SweetAlertDialog.ERROR_TYPE)
.setTitleText(getString(R.string.oops))
.setContentText(getString(R.string.oops_file_does_not_exist))
.show();
break;
case 3:
pDialog.show();
pDialog.setCancelable(false);
break;
case 4:
AlertDialog.Builder editDialog = new AlertDialog.Builder(new ContextThemeWrapper(getActivity(), R.style.AppCompatAlertDialogStyle));
editDialog.setTitle(getResources().getString(R.string.bookname));
pDialog.hide();
final EditText editText = new EditText(getActivity());
editText.setText(booknameString);
editDialog.setView(editText);
editDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
// do something when the button is clicked
public void onClick(DialogInterface arg0, int arg1) {
booknameString = editText.getText().toString();
pDialog.show();
pDialog.setCancelable(false);
synchronized (syncToken) {
syncToken.notify();
}
}
});
editDialog.show();
break;
case 5:
pDialog.getProgressHelper().setInstantProgress(progressNum);
break;
case 6:
pDialog.hide();
new SweetAlertDialog(getActivity(), SweetAlertDialog.ERROR_TYPE)
.setTitleText(getString(R.string.illegal_filename))
.setConfirmText("OK")
.show();
break;
}
super.handleMessage(msg);
}
};
@Override
public void onDestroy() {
super.onDestroy();
pDialog.dismiss();
}
@Override
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_STORAGE_ACCESS) {
Uri treeUri = null;
if (resultCode == Activity.RESULT_OK) {
// Get Uri from Storage Access Framework.
treeUri = data.getData();
// Persist URI in shared preference so that you can use it later.
// Use your own framework here instead of PreferenceUtil.
prefs.edit().putString(KeyCollection.KEY_SDCARD_URI, treeUri.toString()).apply();
// Persist access permissions.
getActivity().getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
}
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(KeyCollection.KEY_INPUT_FILE)) {
inputPreference.setSummary(sharedPreferences.getString(KeyCollection.KEY_INPUT_FILE, DialogConfigs.DEFAULT_DIR));
} else if (key.equals(KeyCollection.KEY_OUTPUT_FOLDER)) {
outputPreference.setSummary(sharedPreferences.getString(KeyCollection.KEY_OUTPUT_FOLDER, DialogConfigs.DEFAULT_DIR));
}
}
private OutputStreamWriter getOutputStreamWriter(File outFile) {
FileUtil fileUtil = new FileUtil(getActivity());
OutputStreamWriter osw;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && fileUtil.getExtSdCardFolder(outFile)!=null) {
// in external sdcard
Uri uri;
try {
uri = Uri.parse(prefs.getString(KeyCollection.KEY_SDCARD_URI, null));
} catch (Exception e) {
uri = null;
}
DocumentFile targetDocument = fileUtil.getDocumentFile(outFile, uri);
try {
OutputStream outStream = getActivity().getApplication().
getContentResolver().openOutputStream(targetDocument.getUri());
osw = new OutputStreamWriter(outStream, prefs.getString(KeyCollection.KEY_OUTPUT_ENCODING, "Unicode"));
} catch (Exception e) {
osw = null;
}
} else {
try {
osw = new OutputStreamWriter(new FileOutputStream(outFile), prefs.getString(KeyCollection.KEY_OUTPUT_ENCODING, "Unicode"));
} catch (Exception e) {
osw = null;
}
}
return osw;
}
private File getOutFile(String strPath, String strName, String strExtention) {
File file = new File(strPath + strName + "." +strExtention);
for (int i = 1; file.exists(); ++i) {
file = new File(strPath + strName + "(" + i + ")." + strExtention);
}
return file;
}
private boolean deleteSourceFile(File inFile) {
boolean blRet;
FileUtil fileUtil = new FileUtil(getActivity());
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && fileUtil.getExtSdCardFolder(inFile)!=null) {
// in external sdcard
Uri uri;
try {
uri = Uri.parse(prefs.getString(KeyCollection.KEY_SDCARD_URI, null));
} catch (Exception e) {
uri = null;
}
DocumentFile targetDocument = fileUtil.getDocumentFile(inFile, uri);
blRet = targetDocument.delete();
} else {
blRet = inFile.delete();
}
return blRet;
}
private int countLines(String filename, String encodeString) throws IOException {
File inFile = new File(filename);
InputStream is = new FileInputStream(inFile);
InputStreamReader isr = new InputStreamReader(is, encodeString);
BufferedReader bReader = new BufferedReader(isr);
int count = 0;
while((bReader.readLine()) != null) {
++count;
}
bReader.close();
isr.close();
is.close();
return count;
}
private String getFileExtension(File inFile) {
int startIndex = inFile.getName().lastIndexOf(46) + 1;
int endIndex = inFile.getName().length();
String file_extension = inFile.getName().substring(startIndex, endIndex);
return file_extension;
}
private boolean isFilenameValid(String fileName) {
File f = new File(fileName);
try {
return f.getCanonicalFile().getName().equals(fileName);
} catch (IOException e) {
return false;
}
}
private void prepareFileList(File testDirFile, ArrayList<String> fileList) {
if(testDirFile.isDirectory()) {
String[] filenames = testDirFile.list();
for (int i = 0 ; i < filenames.length ; ++i){
File tempFile = new File(testDirFile.getAbsolutePath() + "/" + filenames[i]);
String file_extension = getFileExtension(tempFile);
if(!tempFile.isDirectory() && contain(filter, file_extension)){
fileList.add(tempFile.getAbsolutePath());
}
}
} else {
fileList.add(testDirFile.getAbsolutePath());
}
}
private String detectEncode(String testFileName) {
String encodeString;
try {
if (prefs.getString(KeyCollection.KEY_ENCODING, "0").equals("0")) {
FileInputStream fis = new FileInputStream(testFileName);
byte[] buf = new byte[4096];
UniversalDetector detector = new UniversalDetector(null);
int nread;
while ((nread = fis.read(buf)) > 0 && !detector.isDone()) {
detector.handleData(buf, 0, nread);
}
detector.dataEnd();
encodeString = detector.getDetectedCharset();
if (encodeString == null) {
encodeString = "Unicode";
}
detector.reset();
fis.close();
} else {
encodeString = prefs.getString(KeyCollection.KEY_ENCODING, "UTF-8");
}
} catch (Exception e) {
encodeString = prefs.getString(KeyCollection.KEY_ENCODING, "UTF-8");
}
return encodeString;
}
private String getFileName(File inFile) {
String name = inFile.getName();
int pos = name.lastIndexOf(".");
if (pos > 0) {
name = name.substring(0, pos);
}
return name;
}
/**
* Check the folder for writeability. If not, then on Android 5 retrieve Uri for extsdcard via Storage
* Access Framework.
*
* @param folder The folder to be checked.
* @return true if the check was successful or if SAF has been triggered.
* false trigger SAF or in kikat.
*/
private boolean checkFolder(@NonNull final File folder) {
FileUtil fileUtil = new FileUtil(getActivity().getApplicationContext());
Uri uri;
try {
uri = Uri.parse(prefs.getString(KeyCollection.KEY_SDCARD_URI, null));
} catch (Exception e) {
uri = null;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && fileUtil.isOnExtSdCard(folder)) {
// On Android 5, trigger storage access framework.
if (!fileUtil.isWritableNormalOrSaf(folder, uri)) {
return false;
}
// Only accept after SAF stuff is done.
return true;
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
if (fileUtil.isOnExtSdCard(folder) && !fileUtil.isWritableNormal(folder)) {
// The file is in the external sdcard, and Kitkat is bad.
return false;
}
return true;
} else if (FileUtil.isWritable(new File(folder, "DummyFile"))) {
return true;
} else {
// some unknown error
return false;
}
}
/**
* Trigger the storage access framework to access the base folder of the ext sd card.
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void triggerStorageAccessFramework() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, REQUEST_CODE_STORAGE_ACCESS);
}
}