package com.amaze.filemanager.utils; import android.Manifest; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Color; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.support.annotation.StringRes; import android.support.design.widget.BottomSheetDialogFragment; import android.support.v4.app.ActivityCompat; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; import com.amaze.filemanager.R; import com.amaze.filemanager.activities.BaseActivity; import com.amaze.filemanager.activities.MainActivity; import com.amaze.filemanager.database.CloudHandler; import com.amaze.filemanager.database.CryptHandler; import com.amaze.filemanager.database.EncryptedEntry; import com.amaze.filemanager.filesystem.BaseFile; import com.amaze.filemanager.filesystem.FileUtil; import com.amaze.filemanager.filesystem.HFile; import com.amaze.filemanager.filesystem.Operations; import com.amaze.filemanager.fragments.CloudSheetFragment; import com.amaze.filemanager.fragments.MainFragment; import com.amaze.filemanager.fragments.SearchAsyncHelper; import com.amaze.filemanager.fragments.TabFragment; import com.amaze.filemanager.services.DeleteTask; import com.amaze.filemanager.services.ExtractService; import com.amaze.filemanager.services.ZipTask; import com.amaze.filemanager.ui.dialogs.GeneralDialogCreation; import com.amaze.filemanager.utils.provider.UtilitiesProviderInterface; import java.io.File; import java.util.ArrayList; /** * Created by root on 11/22/15, modified by Emmanuel Messulam<emmanuelbendavid@gmail.com> */ public class MainActivityHelper { public static final int NEW_FOLDER = 0, NEW_FILE = 1, NEW_SMB = 2, NEW_CLOUD = 3; private MainActivity mainActivity; private Futils utils; /* * A static string which saves the last searched query. Used to retain search task after * user presses back button from pressing on any list item of search results */ public static String SEARCH_TEXT; public MainActivityHelper(MainActivity mainActivity) { this.mainActivity = mainActivity; this.utils = mainActivity.getFutils(); } public void showFailedOperationDialog(ArrayList<BaseFile> failedOps, boolean move, Context contextc) { MaterialDialog.Builder mat=new MaterialDialog.Builder(contextc); mat.title(contextc.getString(R.string.operationunsuccesful)); mat.theme(mainActivity.getAppTheme().getMaterialDialogTheme()); mat.positiveColor(Color.parseColor(BaseActivity.accentSkin)); mat.positiveText(R.string.cancel); String content = contextc.getResources().getString(R.string.operation_fail_following); int k=1; for(BaseFile s:failedOps){ content=content+ "\n" + (k) + ". " + s.getName(); k++; } mat.content(content); mat.build().show(); } public final BroadcastReceiver mNotificationReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent != null) { if (intent.getAction().equals(Intent.ACTION_MEDIA_MOUNTED)) { Toast.makeText(mainActivity, "Media Mounted", Toast.LENGTH_SHORT).show(); String a = intent.getData().getPath(); if (a != null && a.trim().length() != 0 && new File(a).exists() && new File(a).canExecute()) { MainActivity.dataUtils.getStorages().add(a); mainActivity.refreshDrawer(); } else { mainActivity.refreshDrawer(); } } else if (intent.getAction().equals(Intent.ACTION_MEDIA_UNMOUNTED)) { mainActivity.refreshDrawer(); } } } }; /** * Prompt a dialog to user to input directory name * * @param openMode * @param path current path at which directory to create * @param ma {@link MainFragment} current fragment */ void mkdir(final OpenMode openMode, final String path, final MainFragment ma) { mk(R.string.newfolder, new OnClickMaterialListener() { @Override public void onClick(MaterialDialog materialDialog) { String a = materialDialog.getInputEditText().getText().toString(); mkDir(new HFile(openMode, path + "/" + a), ma); materialDialog.dismiss(); } }); } /** * Prompt a dialog to user to input file name * * @param openMode * @param path current path at which file to create * @param ma {@link MainFragment} current fragment */ void mkfile(final OpenMode openMode, final String path, final MainFragment ma) { mk(R.string.newfile, new OnClickMaterialListener() { @Override public void onClick(MaterialDialog materialDialog) { String a = materialDialog.getInputEditText().getText().toString(); mkFile(new HFile(openMode, path + "/" + a), ma); materialDialog.dismiss(); } }); } private void mk(@StringRes int newText, final OnClickMaterialListener l) { final MaterialDialog materialDialog = GeneralDialogCreation.showNameDialog(mainActivity, new String[]{mainActivity.getResources().getString(R.string.entername), "", mainActivity.getResources().getString(newText), mainActivity.getResources().getString(R.string.create), mainActivity.getResources().getString(R.string.cancel), null}); materialDialog.getActionButton(DialogAction.POSITIVE).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { l.onClick(materialDialog); } }); materialDialog.show(); } private interface OnClickMaterialListener { void onClick(MaterialDialog materialDialog); } public void add(int pos) { final MainFragment ma = (MainFragment) ((TabFragment) mainActivity.getSupportFragmentManager().findFragmentById(R.id.content_frame)).getTab(); final String path = ma.CURRENT_PATH; switch (pos) { case NEW_FOLDER: mkdir(ma.openMode, path, ma); break; case NEW_FILE: mkfile(ma.openMode, path, ma); break; case NEW_CLOUD: BottomSheetDialogFragment fragment = new CloudSheetFragment(); fragment.show(ma.getActivity().getSupportFragmentManager(), CloudSheetFragment.TAG_FRAGMENT); break; } } public String getIntegralNames(String path) { String newPath = ""; switch (Integer.parseInt(path)) { case 0: newPath = mainActivity.getResources().getString(R.string.images); break; case 1: newPath = mainActivity.getResources().getString(R.string.videos); break; case 2: newPath = mainActivity.getResources().getString(R.string.audio); break; case 3: newPath = mainActivity.getResources().getString(R.string.documents); break; case 4: newPath = mainActivity.getResources().getString(R.string.apks); break; case 5: newPath = mainActivity.getResources().getString(R.string.quick); break; case 6: newPath = mainActivity.getResources().getString(R.string.recent); break; } return newPath; } public void guideDialogForLEXA(String path) { final MaterialDialog.Builder x = new MaterialDialog.Builder(mainActivity); x.theme(mainActivity.getAppTheme().getMaterialDialogTheme()); x.title(R.string.needsaccess); LayoutInflater layoutInflater = (LayoutInflater) mainActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = layoutInflater.inflate(R.layout.lexadrawer, null); x.customView(view, true); // textView TextView textView = (TextView) view.findViewById(R.id.description); textView.setText(mainActivity.getResources().getString(R.string.needsaccesssummary) + path + mainActivity.getResources().getString(R.string.needsaccesssummary1)); ((ImageView) view.findViewById(R.id.icon)).setImageResource(R.drawable.sd_operate_step); x.positiveText(R.string.open); x.negativeText(R.string.cancel); x.positiveColor(Color.parseColor(BaseActivity.accentSkin)); x.negativeColor(Color.parseColor(BaseActivity.accentSkin)); x.callback(new MaterialDialog.ButtonCallback() { @Override public void onPositive(MaterialDialog materialDialog) { triggerStorageAccessFramework(); } @Override public void onNegative(MaterialDialog materialDialog) { Toast.makeText(mainActivity, R.string.error, Toast.LENGTH_SHORT).show(); } }); final MaterialDialog y = x.build(); y.show(); } private void triggerStorageAccessFramework() { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); mainActivity.startActivityForResult(intent, 3); } public void rename(OpenMode mode, final String oldPath, final String newPath, final Activity context, boolean rootmode) { final Toast toast=Toast.makeText(context, context.getString(R.string.renaming), Toast.LENGTH_SHORT); toast.show(); Operations.rename(new HFile(mode, oldPath), new HFile(mode, newPath), rootmode, context, new Operations.ErrorCallBack() { @Override public void exists(HFile file) { context.runOnUiThread(new Runnable() { @Override public void run() { if (toast != null) toast.cancel(); Toast.makeText(mainActivity, context.getString(R.string.fileexist), Toast.LENGTH_SHORT).show(); } }); } @Override public void launchSAF(HFile file) { } @Override public void launchSAF(final HFile file, final HFile file1) { context.runOnUiThread(new Runnable() { @Override public void run() { if (toast != null) toast.cancel(); mainActivity.oppathe = file.getPath(); mainActivity.oppathe1 = file1.getPath(); mainActivity.operation = DataUtils.RENAME; guideDialogForLEXA(mainActivity.oppathe1); } }); } @Override public void done(HFile hFile, final boolean b) { context.runOnUiThread(new Runnable() { @Override public void run() { if (b) { Intent intent = new Intent("loadlist"); mainActivity.sendBroadcast(intent); // update the database entry to reflect rename for encrypted file if (oldPath.endsWith(CryptUtil.CRYPT_EXTENSION)) { try { CryptHandler cryptHandler = new CryptHandler(context); EncryptedEntry oldEntry = cryptHandler.findEntry(oldPath); EncryptedEntry newEntry = new EncryptedEntry(); newEntry.setId(oldEntry.getId()); newEntry.setPassword(oldEntry.getPassword()); newEntry.setPath(newPath); cryptHandler.updateEntry(oldEntry, newEntry); } catch (Exception e) { e.printStackTrace(); // couldn't change the entry, leave it alone } } } else Toast.makeText(context, context.getString(R.string.operationunsuccesful), Toast.LENGTH_SHORT).show(); } }); } @Override public void invalidName(final HFile file) { context.runOnUiThread(new Runnable() { @Override public void run() { if (toast != null) toast.cancel(); Toast.makeText(context, context.getString(R.string.invalid_name) + ": " + file.getName(), Toast.LENGTH_LONG).show(); } }); } }); } public static final int DOESNT_EXIST = 0; public static final int WRITABLE_OR_ON_SDCARD = 1; //For Android 5 public static final int CAN_CREATE_FILES = 2; public int checkFolder(final File folder, Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (FileUtil.isOnExtSdCard(folder, context)) { if (!folder.exists() || !folder.isDirectory()) { return DOESNT_EXIST; } // On Android 5, trigger storage access framework. if (!FileUtil.isWritableNormalOrSaf(folder, context)) { guideDialogForLEXA(folder.getPath()); return CAN_CREATE_FILES; } return WRITABLE_OR_ON_SDCARD; } else if (FileUtil.isWritable(new File(folder, "DummyFile"))) { return WRITABLE_OR_ON_SDCARD; } else return DOESNT_EXIST; } else if (Build.VERSION.SDK_INT == 19) { if (FileUtil.isOnExtSdCard(folder, context)) { // Assume that Kitkat workaround works return WRITABLE_OR_ON_SDCARD; } else if (FileUtil.isWritable(new File(folder, "DummyFile"))) { return WRITABLE_OR_ON_SDCARD; } else return DOESNT_EXIST; } else if (FileUtil.isWritable(new File(folder, "DummyFile"))) { return WRITABLE_OR_ON_SDCARD; } else { return DOESNT_EXIST; } } /** * Helper method to start Compress service * * @param file the new compressed file * @param baseFiles list of {@link BaseFile} to be compressed */ public void compressFiles(File file, ArrayList<BaseFile> baseFiles) { int mode = checkFolder(file.getParentFile(), mainActivity); if (mode == 2) { mainActivity.oppathe = (file.getPath()); mainActivity.operation = DataUtils.COMPRESS; mainActivity.oparrayList = baseFiles; } else if (mode == 1) { Intent intent2 = new Intent(mainActivity, ZipTask.class); intent2.putExtra(ZipTask.KEY_COMPRESS_PATH, file.getPath()); intent2.putExtra(ZipTask.KEY_COMPRESS_FILES, baseFiles); ServiceWatcherUtil.runService(mainActivity, intent2); } else Toast.makeText(mainActivity, R.string.not_allowed, Toast.LENGTH_SHORT).show(); } public void mkFile(final HFile path, final MainFragment ma) { final Toast toast = Toast.makeText(ma.getActivity(), ma.getString(R.string.creatingfile), Toast.LENGTH_SHORT); toast.show(); Operations.mkfile(path, ma.getActivity(), BaseActivity.rootMode, new Operations.ErrorCallBack() { @Override public void exists(final HFile file) { ma.getActivity().runOnUiThread(new Runnable() { @Override public void run() { if (toast != null) toast.cancel(); Toast.makeText(mainActivity, mainActivity.getString(R.string.fileexist), Toast.LENGTH_SHORT).show(); if (ma != null && ma.getActivity() != null) { // retry with dialog prompted again mkfile(file.getMode(), file.getParent(), ma); } } }); } @Override public void launchSAF(HFile file) { ma.getActivity().runOnUiThread(new Runnable() { @Override public void run() { if (toast != null) toast.cancel(); mainActivity.oppathe = path.getPath(); mainActivity.operation = DataUtils.NEW_FILE; guideDialogForLEXA(mainActivity.oppathe); } }); } @Override public void launchSAF(HFile file, HFile file1) { } @Override public void done(HFile hFile, final boolean b) { ma.getActivity().runOnUiThread(new Runnable() { @Override public void run() { if (b) { ma.updateList(); } else Toast.makeText(ma.getActivity(), ma.getString(R.string.operationunsuccesful), Toast.LENGTH_SHORT).show(); } }); } @Override public void invalidName(final HFile file) { ma.getActivity().runOnUiThread(new Runnable() { @Override public void run() { if (toast != null) toast.cancel(); Toast.makeText(ma.getActivity(), ma.getString(R.string.invalid_name) + ": " + file.getName(), Toast.LENGTH_LONG).show(); } }); } }); } public void mkDir(final HFile path, final MainFragment ma) { final Toast toast = Toast.makeText(ma.getActivity(), ma.getString(R.string.creatingfolder), Toast.LENGTH_SHORT); toast.show(); Operations.mkdir(path, ma.getActivity(), BaseActivity.rootMode, new Operations.ErrorCallBack() { @Override public void exists(final HFile file) { ma.getActivity().runOnUiThread(new Runnable() { @Override public void run() { if (toast != null) toast.cancel(); Toast.makeText(mainActivity, mainActivity.getString(R.string.fileexist), Toast.LENGTH_SHORT).show(); if (ma != null && ma.getActivity() != null) { // retry with dialog prompted again mkdir(file.getMode(), file.getParent(), ma); } } }); } @Override public void launchSAF(HFile file) { if (toast != null) toast.cancel(); ma.getActivity().runOnUiThread(new Runnable() { @Override public void run() { mainActivity.oppathe = path.getPath(); mainActivity.operation = DataUtils.NEW_FOLDER; guideDialogForLEXA(mainActivity.oppathe); } }); } @Override public void launchSAF(HFile file, HFile file1) { } @Override public void done(HFile hFile, final boolean b) { ma.getActivity().runOnUiThread(new Runnable() { @Override public void run() { if (b) { ma.updateList(); } else Toast.makeText(ma.getActivity(), ma.getString(R.string.operationunsuccesful), Toast.LENGTH_SHORT).show(); } }); } @Override public void invalidName(final HFile file) { ma.getActivity().runOnUiThread(new Runnable() { @Override public void run() { if (toast != null) toast.cancel(); Toast.makeText(ma.getActivity(), ma.getString(R.string.invalid_name) + ": " + file.getName(), Toast.LENGTH_LONG).show(); } }); } }); } public void deleteFiles(ArrayList<BaseFile> files) { if (files == null) return; if (files.get(0).isSmb()) { new DeleteTask(null, mainActivity).execute((files)); return; } int mode = checkFolder(new File(files.get(0).getPath()).getParentFile(), mainActivity); if (mode == 2) { mainActivity.oparrayList = (files); mainActivity.operation = DataUtils.DELETE; } else if (mode == 1 || mode == 0) new DeleteTask(null, mainActivity).execute((files)); else Toast.makeText(mainActivity, R.string.not_allowed, Toast.LENGTH_SHORT).show(); } public void extractFile(File file) { int mode = checkFolder(file.getParentFile(), mainActivity); if (mode == 2) { mainActivity.oppathe = (file.getPath()); mainActivity.operation = DataUtils.EXTRACT; } else if (mode == 1) { Intent intent = new Intent(mainActivity, ExtractService.class); intent.putExtra(ExtractService.KEY_PATH_ZIP, file.getPath()); ServiceWatcherUtil.runService(mainActivity, intent); } else Toast.makeText(mainActivity, R.string.not_allowed, Toast.LENGTH_SHORT).show(); } public String parseSmbPath(String a) { if (a.contains("@")) return "smb://" + a.substring(a.indexOf("@") + 1, a.length()); else return a; } /** * Retrieve a path with {@link OTGUtil#PREFIX_OTG} as prefix * @param path * @return */ public String parseOTGPath(String path) { if (path.contains(OTGUtil.PREFIX_OTG)) return path; else return OTGUtil.PREFIX_OTG + path.substring(path.indexOf(":") + 1, path.length()); } public String parseCloudPath(OpenMode serviceType, String path) { switch (serviceType) { case DROPBOX: if (path.contains(CloudHandler.CLOUD_PREFIX_DROPBOX)) return path; else return CloudHandler.CLOUD_PREFIX_DROPBOX + path.substring(path.indexOf(":") + 1, path.length()); case BOX: if (path.contains(CloudHandler.CLOUD_PREFIX_BOX)) return path; else return CloudHandler.CLOUD_PREFIX_BOX + path.substring(path.indexOf(":") + 1, path.length()); case GDRIVE: if (path.contains(CloudHandler.CLOUD_PREFIX_GOOGLE_DRIVE)) return path; else return CloudHandler.CLOUD_PREFIX_GOOGLE_DRIVE + path.substring(path.indexOf(":") + 1, path.length()); case ONEDRIVE: if (path.contains(CloudHandler.CLOUD_PREFIX_ONE_DRIVE)) return path; else return CloudHandler.CLOUD_PREFIX_ONE_DRIVE + path.substring(path.indexOf(":") + 1, path.length()); default: return path; } } /** * Creates a fragment which will handle the search AsyncTask {@link SearchAsyncHelper} * * @param query the text query entered the by user */ public void search(String query) { TabFragment tabFragment = mainActivity.getFragment(); if (tabFragment == null) return; final MainFragment ma = (MainFragment) tabFragment.getTab(); final String fpath = ma.CURRENT_PATH; /*SearchTask task = new SearchTask(ma.searchHelper, ma, query); task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, fpath);*/ //ma.searchTask = task; SEARCH_TEXT = query; mainActivity.mainFragment = (MainFragment) mainActivity.getFragment().getTab(); FragmentManager fm = mainActivity.getSupportFragmentManager(); SearchAsyncHelper fragment = (SearchAsyncHelper) fm.findFragmentByTag(MainActivity.TAG_ASYNC_HELPER); if (fragment != null) { if (fragment.mSearchTask.getStatus() == AsyncTask.Status.RUNNING) { fragment.mSearchTask.cancel(true); } fm.beginTransaction().remove(fragment).commit(); } addSearchFragment(fm, new SearchAsyncHelper(), fpath, query, ma.openMode, BaseActivity.rootMode, mainActivity.sharedPref.getBoolean(SearchAsyncHelper.KEY_REGEX, false), mainActivity.sharedPref.getBoolean(SearchAsyncHelper.KEY_REGEX_MATCHES, false)); } /** * Adds a search fragment that can persist it's state on config change * * @param fragmentManager fragmentManager * @param fragment current fragment * @param path current path * @param input query typed by user * @param openMode defines the file type * @param rootMode is root enabled * @param regex is regular expression search enabled * @param matches is matches enabled for patter matching */ public static void addSearchFragment(FragmentManager fragmentManager, Fragment fragment, String path, String input, OpenMode openMode, boolean rootMode, boolean regex, boolean matches) { Bundle args = new Bundle(); args.putString(SearchAsyncHelper.KEY_INPUT, input); args.putString(SearchAsyncHelper.KEY_PATH, path); args.putInt(SearchAsyncHelper.KEY_OPEN_MODE, openMode.ordinal()); args.putBoolean(SearchAsyncHelper.KEY_ROOT_MODE, rootMode); args.putBoolean(SearchAsyncHelper.KEY_REGEX, regex); args.putBoolean(SearchAsyncHelper.KEY_REGEX_MATCHES, matches); fragment.setArguments(args); fragmentManager.beginTransaction().add(fragment, MainActivity.TAG_ASYNC_HELPER).commit(); } /** * Check whether creation of new directory is inside the same directory with the same name or not * Directory inside the same directory with similar filename shall not be allowed * Doesn't work at an OTG path * * @param file * @return */ public static boolean isNewDirectoryRecursive(HFile file) { return file.getName().equals(file.getParentName()); } public static boolean checkAccountsPermission(Context context) { // Verify that all required contact permissions have been granted. if (ActivityCompat.checkSelfPermission(context, Manifest.permission.GET_ACCOUNTS) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(context, Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED) { return false; } return true; } public static void requestAccountsPermission(final Activity activity) { final String[] PERMISSIONS = {Manifest.permission.GET_ACCOUNTS, Manifest.permission.INTERNET}; if (ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.GET_ACCOUNTS) || ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.INTERNET)) { // Provide an additional rationale to the user if the permission was not granted // and the user would benefit from additional context for the use of the permission. // For example, if the request has been denied previously. String fab_skin = (BaseActivity.accentSkin); final MaterialDialog materialDialog = GeneralDialogCreation.showBasicDialog(activity, fab_skin, ((UtilitiesProviderInterface) activity).getAppTheme(), new String[] { activity.getResources().getString(R.string.grantgplus), activity.getResources().getString(R.string.grantper), activity.getResources().getString(R.string.grant), activity.getResources().getString(R.string.cancel), null }); materialDialog.getActionButton(DialogAction.POSITIVE).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ActivityCompat.requestPermissions(activity,PERMISSIONS, 66); materialDialog.dismiss(); } }); materialDialog.getActionButton(DialogAction.NEGATIVE).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { activity.finish(); } }); materialDialog.setCancelable(false); materialDialog.show(); } else { // Contact permissions have not been granted yet. Request them directly. ActivityCompat.requestPermissions(activity, PERMISSIONS, 66); } } }