package net.minetest.minetest; import android.annotation.SuppressLint; import android.app.Activity; import android.content.res.AssetFileDescriptor; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.util.Log; import android.view.Display; import android.view.View; import android.widget.ProgressBar; import android.widget.TextView; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.util.Vector; public class MinetestAssetCopy extends Activity { private ProgressBar m_ProgressBar; private TextView m_Filename; private copyAssetTask m_AssetCopy; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.assetcopy); m_ProgressBar = findViewById(R.id.progressBar1); m_Filename = findViewById(R.id.textView1); Display display = getWindowManager().getDefaultDisplay(); m_ProgressBar.getLayoutParams().width = (int) (display.getWidth() * 0.8); m_ProgressBar.invalidate(); /* check if there's already a copy in progress and reuse in case it is*/ MinetestAssetCopy prevActivity = (MinetestAssetCopy) getLastNonConfigurationInstance(); if (prevActivity != null) { m_AssetCopy = prevActivity.m_AssetCopy; } else { m_AssetCopy = new copyAssetTask(); m_AssetCopy.execute(); } } @Override protected void onResume() { super.onResume(); makeFullScreen(); } @Override protected void onDestroy() { super.onDestroy(); if (m_AssetCopy != null) { m_AssetCopy.cancel(true); } } private void makeFullScreen() { if (Build.VERSION.SDK_INT >= 19) this.getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus) makeFullScreen(); } /* preserve asset copy background task to prevent restart of copying */ /* this way of doing it is not recommended for latest android version */ /* but the recommended way isn't available on android 2.x */ public Object onRetainNonConfigurationInstance() { return this; } @SuppressLint("StaticFieldLeak") private class copyAssetTask extends AsyncTask { boolean m_copy_started = false; String m_Foldername = "media"; Vector m_foldernames; Vector m_filenames; Vector m_tocopy; Vector m_asset_size_unknown; private long getFullSize(String filename) { long size = 0; try { InputStream src = getAssets().open(filename); byte[] buf = new byte[4096]; int len; while ((len = src.read(buf)) > 0) { size += len; } } catch (IOException e) { e.printStackTrace(); } return size; } @Override protected String doInBackground(String... files) { m_foldernames = new Vector<>(); m_filenames = new Vector<>(); m_tocopy = new Vector<>(); m_asset_size_unknown = new Vector<>(); String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath() + "/"; // prepare temp folder File TempFolder = new File(baseDir + "Minetest/tmp/"); if (!TempFolder.exists()) { TempFolder.mkdir(); } else { File[] todel = TempFolder.listFiles(); for (File file : todel) { Log.v("MinetestAssetCopy", "deleting: " + file.getAbsolutePath()); file.delete(); } } // add a .nomedia file try { OutputStream dst = new FileOutputStream(baseDir + "Minetest/.nomedia"); dst.close(); } catch (IOException e) { Log.e("MinetestAssetCopy", "Failed to create .nomedia file"); e.printStackTrace(); } // build lists from prepared data BuildFolderList(); BuildFileList(); // scan filelist ProcessFileList(); // doing work m_copy_started = true; m_ProgressBar.setMax(m_tocopy.size()); for (int i = 0; i < m_tocopy.size(); i++) { try { String filename = m_tocopy.get(i); publishProgress(i); boolean asset_size_unknown = false; long filesize = -1; if (m_asset_size_unknown.contains(filename)) { File testme = new File(baseDir + "/" + filename); if (testme.exists()) filesize = testme.length(); asset_size_unknown = true; } InputStream src; try { src = getAssets().open(filename); } catch (IOException e) { Log.e("MinetestAssetCopy", "Copying file: " + filename + " FAILED (not in assets)"); e.printStackTrace(); continue; } // Transfer bytes from in to out byte[] buf = new byte[1024]; int len = src.read(buf, 0, 1024); /* following handling is crazy but we need to deal with */ /* compressed assets.Flash chips limited livetime due to */ /* write operations, we can't allow large files to destroy */ /* users flash. */ if (asset_size_unknown) { if ((len > 0) && (len < buf.length) && (len == filesize)) { src.close(); continue; } if (len == buf.length) { src.close(); long size = getFullSize(filename); if (size == filesize) { continue; } src = getAssets().open(filename); len = src.read(buf, 0, 1024); } } if (len > 0) { int total_filesize = 0; OutputStream dst; try { dst = new FileOutputStream(baseDir + "/" + filename); } catch (IOException e) { Log.e("MinetestAssetCopy", "Copying file: " + baseDir + "/" + filename + " FAILED (couldn't open output file)"); e.printStackTrace(); src.close(); continue; } dst.write(buf, 0, len); total_filesize += len; while ((len = src.read(buf)) > 0) { dst.write(buf, 0, len); total_filesize += len; } dst.close(); Log.v("MinetestAssetCopy", "Copied file: " + m_tocopy.get(i) + " (" + total_filesize + " bytes)"); } else if (len < 0) { Log.e("MinetestAssetCopy", "Copying file: " + m_tocopy.get(i) + " failed, size < 0"); } src.close(); } catch (IOException e) { Log.e("MinetestAssetCopy", "Copying file: " + m_tocopy.get(i) + " failed"); e.printStackTrace(); } } return ""; } /** * update progress bar */ protected void onProgressUpdate(Integer... progress) { if (m_copy_started) { String todisplay = m_tocopy.get(progress[0]); m_ProgressBar.setProgress(progress[0]); m_Filename.setText(todisplay); } else { String todisplay = m_Foldername; String full_text = "scanning " + todisplay + " ..."; m_Filename.setText(full_text); } } /** * check all files and folders in filelist */ void ProcessFileList() { String FlashBaseDir = Environment.getExternalStorageDirectory().getAbsolutePath(); for (String current_path : m_filenames) { String FlashPath = FlashBaseDir + "/" + current_path; if (isAssetFolder(current_path)) { /* store information and update gui */ m_Foldername = current_path; publishProgress(0); /* open file in order to check if it's a folder */ File current_folder = new File(FlashPath); if (!current_folder.exists()) { if (!current_folder.mkdirs()) { Log.e("MinetestAssetCopy", "\t failed create folder: " + FlashPath); } else { Log.v("MinetestAssetCopy", "\t created folder: " + FlashPath); } } continue; } /* if it's not a folder it's most likely a file */ boolean refresh = true; File testme = new File(FlashPath); long asset_filesize = -1; long stored_filesize; if (testme.exists()) { try { AssetFileDescriptor fd = getAssets().openFd(current_path); asset_filesize = fd.getLength(); fd.close(); } catch (IOException e) { m_asset_size_unknown.add(current_path); Log.e("MinetestAssetCopy", "Failed to open asset file \"" + FlashPath + "\" for size check"); } stored_filesize = testme.length(); if (asset_filesize == stored_filesize) refresh = false; } if (refresh) m_tocopy.add(current_path); } } /** * read list of folders prepared on package build */ void BuildFolderList() { try { InputStream is = getAssets().open("index.txt"); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); String line = reader.readLine(); while (line != null) { m_foldernames.add(line); line = reader.readLine(); } is.close(); } catch (IOException e1) { Log.e("MinetestAssetCopy", "Error on processing index.txt"); e1.printStackTrace(); } } /** * read list of asset files prepared on package build */ void BuildFileList() { long entrycount = 0; try { InputStream is = getAssets().open("filelist.txt"); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); String line = reader.readLine(); while (line != null) { m_filenames.add(line); line = reader.readLine(); entrycount++; } is.close(); } catch (IOException e1) { Log.e("MinetestAssetCopy", "Error on processing filelist.txt"); e1.printStackTrace(); } } protected void onPostExecute(String result) { finish(); } boolean isAssetFolder(String path) { return m_foldernames.contains(path); } } }