diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 512c456b..f253cdf7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,8 +4,6 @@ package="org.meowcat.edxposed.manager"> - - @@ -98,7 +96,7 @@ = Build.VERSION_CODES.M) { - requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, XposedApp.WRITE_EXTERNAL_PERMISSION); - } - return true; - } - return false; - } - void reboot(String mode) { if (!Shell.rootAccess()) { showAlert(getString(R.string.root_failed)); diff --git a/app/src/main/java/org/meowcat/edxposed/manager/BaseAdvancedInstaller.java b/app/src/main/java/org/meowcat/edxposed/manager/BaseAdvancedInstaller.java index 4c4b4de6..f762dfd0 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/BaseAdvancedInstaller.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/BaseAdvancedInstaller.java @@ -1,13 +1,9 @@ package org.meowcat.edxposed.manager; -import android.Manifest; import android.content.DialogInterface; import android.content.Intent; -import android.content.pm.PackageManager; import android.net.Uri; -import android.os.Build; import android.os.Bundle; -import android.os.Handler; import android.text.Html; import android.view.LayoutInflater; import android.view.View; @@ -20,11 +16,9 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; -import androidx.core.app.ActivityCompat; import androidx.fragment.app.Fragment; import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import com.google.android.material.snackbar.Snackbar; import org.meowcat.edxposed.manager.util.NavUtil; import org.meowcat.edxposed.manager.util.json.XposedTab; @@ -33,8 +27,6 @@ import org.meowcat.edxposed.manager.util.json.XposedZip; import java.util.List; import java.util.Objects; -import static org.meowcat.edxposed.manager.XposedApp.WRITE_EXTERNAL_PERMISSION; - public class BaseAdvancedInstaller extends Fragment { private View mClickedButton; @@ -88,16 +80,6 @@ public class BaseAdvancedInstaller extends Fragment { return Objects.requireNonNull(tab).description; } - private boolean checkPermissions() { - if (Build.VERSION.SDK_INT < 23) return false; - - if (ActivityCompat.checkSelfPermission(Objects.requireNonNull(getActivity()), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, WRITE_EXTERNAL_PERMISSION); - return true; - } - return false; - } - @Override public void onDestroy() { super.onDestroy(); @@ -143,31 +125,21 @@ public class BaseAdvancedInstaller extends Fragment { .setMessage(s).setPositiveButton(android.R.string.ok, null).show(); }); - btnInstall.setOnClickListener(v -> { - mClickedButton = v; - if (checkPermissions()) return; + btnInstall.setOnClickListener(v -> areYouSure(R.string.warningArchitecture, + (dialog, which) -> { + XposedZip selectedInstaller = (XposedZip) chooserInstallers.getSelectedItem(); + Uri uri = Uri.parse(selectedInstaller.link); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + startActivity(intent); + })); - areYouSure(R.string.warningArchitecture, - (dialog, which) -> { - XposedZip selectedInstaller = (XposedZip) chooserInstallers.getSelectedItem(); - Uri uri = Uri.parse(selectedInstaller.link); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - startActivity(intent); - }); - }); - - btnUninstall.setOnClickListener(v -> { - mClickedButton = v; - if (checkPermissions()) return; - - areYouSure(R.string.warningArchitecture, - (dialog, which) -> { - XposedZip selectedUninstaller = (XposedZip) chooserUninstallers.getSelectedItem(); - Uri uri = Uri.parse(selectedUninstaller.link); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - startActivity(intent); - }); - }); + btnUninstall.setOnClickListener(v -> areYouSure(R.string.warningArchitecture, + (dialog, which) -> { + XposedZip selectedUninstaller = (XposedZip) chooserUninstallers.getSelectedItem(); + Uri uri = Uri.parse(selectedUninstaller.link); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + startActivity(intent); + })); noticeTv.setText(Html.fromHtml(notice())); author.setText(getString(R.string.download_author, author())); @@ -198,20 +170,6 @@ public class BaseAdvancedInstaller extends Fragment { return view; } - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (requestCode == WRITE_EXTERNAL_PERMISSION) { - if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - if (mClickedButton != null) { - new Handler().postDelayed(() -> mClickedButton.performClick(), 500); - } - } else { - Snackbar.make(getActivity().findViewById(R.id.snackbar), R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show(); - } - } - } - @SuppressWarnings("SameParameterValue") private void areYouSure(int contentTextId, DialogInterface.OnClickListener listener) { diff --git a/app/src/main/java/org/meowcat/edxposed/manager/BlackListActivity.java b/app/src/main/java/org/meowcat/edxposed/manager/BlackListActivity.java index bdeb92ba..96481189 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/BlackListActivity.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/BlackListActivity.java @@ -11,7 +11,6 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.Toolbar; -import androidx.core.view.MenuItemCompat; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -73,7 +72,7 @@ public class BlackListActivity extends BaseActivity implements AppAdapter.Callba @Override public boolean onCreateOptionsMenu(@NonNull Menu menu) { getMenuInflater().inflate(R.menu.menu_app_list, menu); - mSearchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.menu_search)); + mSearchView = (SearchView) menu.findItem(R.id.menu_search).getActionView(); mSearchView.setOnQueryTextListener(mSearchListener); return super.onCreateOptionsMenu(menu); } @@ -126,11 +125,6 @@ public class BlackListActivity extends BaseActivity implements AppAdapter.Callba AppHelper.showMenu(this, getSupportFragmentManager(), v, info); } - @Override - public void onPointerCaptureChanged(boolean hasCapture) { - - } - @Override public void onBackPressed() { if (mSearchView.isIconified()) { diff --git a/app/src/main/java/org/meowcat/edxposed/manager/CompatListActivity.java b/app/src/main/java/org/meowcat/edxposed/manager/CompatListActivity.java index c94f411b..83b99db2 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/CompatListActivity.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/CompatListActivity.java @@ -10,7 +10,6 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.Toolbar; -import androidx.core.view.MenuItemCompat; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -68,15 +67,10 @@ public class CompatListActivity extends BaseActivity implements AppAdapter.Callb }; } - @Override - public void onResume() { - super.onResume(); - } - @Override public boolean onCreateOptionsMenu(@NonNull Menu menu) { getMenuInflater().inflate(R.menu.menu_app_list, menu); - mSearchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.menu_search)); + mSearchView = (SearchView) menu.findItem(R.id.menu_search).getActionView(); mSearchView.setOnQueryTextListener(mSearchListener); return super.onCreateOptionsMenu(menu); } diff --git a/app/src/main/java/org/meowcat/edxposed/manager/DownloadActivity.java b/app/src/main/java/org/meowcat/edxposed/manager/DownloadActivity.java index 60afee70..1e638dd6 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/DownloadActivity.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/DownloadActivity.java @@ -23,7 +23,6 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.Toolbar; -import androidx.core.view.MenuItemCompat; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -153,8 +152,7 @@ public class DownloadActivity extends BaseActivity implements RepoLoader.RepoLis getMenuInflater().inflate(R.menu.menu_download, menu); // Setup search button - final MenuItem searchItem = menu.findItem(R.id.menu_search); - mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem); + mSearchView = (SearchView) menu.findItem(R.id.menu_search).getActionView(); mSearchView.setIconifiedByDefault(true); mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override diff --git a/app/src/main/java/org/meowcat/edxposed/manager/DownloadDetailsFragment.java b/app/src/main/java/org/meowcat/edxposed/manager/DownloadDetailsFragment.java index 1d511950..17a3f7f2 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/DownloadDetailsFragment.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/DownloadDetailsFragment.java @@ -1,5 +1,6 @@ package org.meowcat.edxposed.manager; +import android.annotation.SuppressLint; import android.net.Uri; import android.os.Bundle; import android.text.method.LinkMovementMethod; @@ -20,6 +21,7 @@ import org.meowcat.edxposed.manager.util.chrome.LinkTransformationMethod; public class DownloadDetailsFragment extends Fragment { + @SuppressLint("SetTextI18n") @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { DownloadDetailsActivity mActivity = (DownloadDetailsActivity) getActivity(); diff --git a/app/src/main/java/org/meowcat/edxposed/manager/DownloadDetailsSettingsFragment.java b/app/src/main/java/org/meowcat/edxposed/manager/DownloadDetailsSettingsFragment.java index bb06d323..7c464bb0 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/DownloadDetailsSettingsFragment.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/DownloadDetailsSettingsFragment.java @@ -40,7 +40,7 @@ public class DownloadDetailsSettingsFragment extends PreferenceFragmentCompat { if (prefs.getBoolean("no_global", true)) { for (Map.Entry k : prefs.getAll().entrySet()) { - if (prefs.getString(k.getKey(), "").equals("global")) { + if (("global").equals(prefs.getString(k.getKey(), ""))) { editor.putString(k.getKey(), "").apply(); } } diff --git a/app/src/main/java/org/meowcat/edxposed/manager/DownloadDetailsVersionsFragment.java b/app/src/main/java/org/meowcat/edxposed/manager/DownloadDetailsVersionsFragment.java index 616358d1..999fdb7f 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/DownloadDetailsVersionsFragment.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/DownloadDetailsVersionsFragment.java @@ -1,12 +1,16 @@ package org.meowcat.edxposed.manager; import android.annotation.SuppressLint; +import android.app.Activity; import android.content.Context; +import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.net.Uri; import android.os.Bundle; import android.text.method.LinkMovementMethod; +import android.util.Log; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; @@ -35,11 +39,11 @@ import org.meowcat.edxposed.manager.util.chrome.LinkTransformationMethod; import org.meowcat.edxposed.manager.widget.DownloadView; import java.io.File; +import java.io.FileInputStream; +import java.io.OutputStream; import java.text.DateFormat; import java.util.Date; -import static org.meowcat.edxposed.manager.XposedApp.WRITE_EXTERNAL_PERMISSION; - public class DownloadDetailsVersionsFragment extends ListFragment { private static View rootView; private DownloadDetailsActivity mActivity; @@ -93,13 +97,31 @@ public class DownloadDetailsVersionsFragment extends ListFragment { } @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (requestCode == WRITE_EXTERNAL_PERMISSION) { - if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - DownloadView.mClickedButton.performClick(); - } else { - Snackbar.make(rootView, R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show(); + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (resultCode != Activity.RESULT_OK) { + return; + } + if (requestCode == 42) { + if (data != null) { + Uri uri = data.getData(); + if (uri != null) { + try { + OutputStream os = mActivity.getContentResolver().openOutputStream(uri); + if (os != null) { + FileInputStream in = new FileInputStream(new File(DownloadView.mInfo.localFilename)); + byte[] buffer = new byte[1024]; + int len; + while ((len = in.read(buffer)) > 0) { + os.write(buffer, 0, len); + } + os.close(); + } + } catch (Exception e) { + e.printStackTrace(); + //Snackbar.make(findViewById(R.id.snackbar), getResources().getString(R.string.logs_save_failed) + "\n" + e.getMessage(), Snackbar.LENGTH_LONG).show(); + } + } } } } diff --git a/app/src/main/java/org/meowcat/edxposed/manager/EdDownloadActivity.java b/app/src/main/java/org/meowcat/edxposed/manager/EdDownloadActivity.java index a4457c2d..4365ff97 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/EdDownloadActivity.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/EdDownloadActivity.java @@ -83,10 +83,6 @@ public class EdDownloadActivity extends BaseActivity { @SuppressLint("StaticFieldLeak") private class JSONParser extends AsyncTask { - private String newApkVersion = null; - private String newApkLink = null; - private String newApkChangelog = null; - @Override protected String doInBackground(Void... params) { try { @@ -115,9 +111,9 @@ public class EdDownloadActivity extends BaseActivity { tabsAdapter.notifyDataSetChanged(); } - newApkVersion = xposedJson.apk.version; - newApkLink = xposedJson.apk.link; - newApkChangelog = xposedJson.apk.changelog; + String newApkVersion = xposedJson.apk.version; + String newApkLink = xposedJson.apk.link; + String newApkChangelog = xposedJson.apk.changelog; if (newApkVersion == null) { return; diff --git a/app/src/main/java/org/meowcat/edxposed/manager/LogsActivity.java b/app/src/main/java/org/meowcat/edxposed/manager/LogsActivity.java index b23c1b68..abb8f01a 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/LogsActivity.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/LogsActivity.java @@ -1,15 +1,11 @@ package org.meowcat.edxposed.manager; -import android.Manifest; import android.annotation.SuppressLint; import android.app.ProgressDialog; import android.content.Intent; -import android.content.pm.PackageManager; import android.net.Uri; import android.os.AsyncTask; -import android.os.Build; import android.os.Bundle; -import android.os.Environment; import android.os.Handler; import android.view.LayoutInflater; import android.view.Menu; @@ -23,7 +19,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; import androidx.appcompat.widget.Toolbar; -import androidx.core.app.ActivityCompat; import androidx.core.content.FileProvider; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -36,6 +31,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; @@ -48,7 +44,6 @@ public class LogsActivity extends BaseActivity { XposedApp.BASE_DIR + "log/error.log.old"); private File mFileAllLog = new File(XposedApp.BASE_DIR + "log/all.log"); private File mFileAllLogOld = new File(XposedApp.BASE_DIR + "log/all.log.old"); - private MenuItem mClickedMenuItem = null; private LogsAdapter mAdapter; private RecyclerView mListView; private Handler handler = new Handler(); @@ -124,7 +119,6 @@ public class LogsActivity extends BaseActivity { @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { - mClickedMenuItem = item; switch (item.getItemId()) { case R.id.menu_scroll_top: scrollTop(); @@ -138,7 +132,8 @@ public class LogsActivity extends BaseActivity { case R.id.menu_send: try { send(); - } catch (NullPointerException ignored) { + } catch (Exception e) { + Snackbar.make(findViewById(R.id.snackbar), e.getLocalizedMessage(), Snackbar.LENGTH_LONG).show(); } return true; case R.id.menu_save: @@ -186,34 +181,8 @@ public class LogsActivity extends BaseActivity { startActivity(Intent.createChooser(sendIntent, getResources().getString(R.string.menuSend))); } - @Override - public void onRequestPermissionsResult(int requestCode, - @NonNull String[] permissions, @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, - grantResults); - if (requestCode == XposedApp.WRITE_EXTERNAL_PERMISSION) { - if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { - if (mClickedMenuItem != null) { - new Handler().postDelayed(() -> onOptionsItemSelected(mClickedMenuItem), 500); - } - } else { - Snackbar.make(findViewById(R.id.snackbar), R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show(); - } - } - } - @SuppressLint("DefaultLocale") private void save() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, XposedApp.WRITE_EXTERNAL_PERMISSION); - return; - } - - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - Snackbar.make(findViewById(R.id.snackbar), R.string.sdcard_not_writable, Snackbar.LENGTH_LONG).show(); - return; - } - Calendar now = Calendar.getInstance(); String filename = String.format( "EdXposed_Verbose_%04d%02d%02d_%02d%02d%02d.log", @@ -221,22 +190,39 @@ public class LogsActivity extends BaseActivity { now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY), now.get(Calendar.MINUTE), now.get(Calendar.SECOND)); - File targetFile = new File(XposedApp.createFolder(), filename); + Intent exportIntent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + exportIntent.addCategory(Intent.CATEGORY_OPENABLE); + exportIntent.setType("text/*"); + exportIntent.putExtra(Intent.EXTRA_TITLE, filename); + startActivityForResult(exportIntent, 42); + } - try { - FileInputStream in = new FileInputStream(allLog ? mFileAllLog : mFileErrorLog); - FileOutputStream out = new FileOutputStream(targetFile); - byte[] buffer = new byte[1024]; - int len; - while ((len = in.read(buffer)) > 0) { - out.write(buffer, 0, len); + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (resultCode != RESULT_OK) { + return; + } + if (requestCode == 42) { + if (data != null) { + Uri uri = data.getData(); + if (uri != null) { + try { + OutputStream os = getContentResolver().openOutputStream(uri); + if (os != null) { + FileInputStream in = new FileInputStream(allLog ? mFileAllLog : mFileErrorLog); + byte[] buffer = new byte[1024]; + int len; + while ((len = in.read(buffer)) > 0) { + os.write(buffer, 0, len); + } + os.close(); + } + } catch (Exception e) { + Snackbar.make(findViewById(R.id.snackbar), getResources().getString(R.string.logs_save_failed) + "\n" + e.getMessage(), Snackbar.LENGTH_LONG).show(); + } + } } - in.close(); - out.close(); - - Snackbar.make(findViewById(R.id.snackbar), targetFile.toString(), Snackbar.LENGTH_LONG).show(); - } catch (IOException e) { - Snackbar.make(findViewById(R.id.snackbar), getResources().getString(R.string.logs_save_failed) + "\n" + e.getMessage(), Snackbar.LENGTH_LONG).show(); } } diff --git a/app/src/main/java/org/meowcat/edxposed/manager/MainActivity.java b/app/src/main/java/org/meowcat/edxposed/manager/MainActivity.java index c3a681e1..171bfeb6 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/MainActivity.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/MainActivity.java @@ -9,8 +9,6 @@ import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.appcompat.view.menu.MenuBuilder; -import androidx.appcompat.view.menu.MenuPopupHelper; import androidx.appcompat.widget.PopupMenu; import com.google.android.material.card.MaterialCardView; @@ -70,9 +68,7 @@ public class MainActivity extends BaseActivity implements RepoLoader.RepoListene PopupMenu appMenu = new PopupMenu(MainActivity.this, menu); appMenu.inflate(R.menu.menu_installer); appMenu.setOnMenuItemClickListener(this::onOptionsItemSelected); - MenuPopupHelper menuHelper = new MenuPopupHelper(MainActivity.this, (MenuBuilder) appMenu.getMenu(), menu); - menuHelper.setForceShowIcon(true); - menuHelper.show(); + appMenu.show(); }); String installedXposedVersion; try { diff --git a/app/src/main/java/org/meowcat/edxposed/manager/ModulesActivity.java b/app/src/main/java/org/meowcat/edxposed/manager/ModulesActivity.java index 1f4fd114..ff8d869b 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/ModulesActivity.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/ModulesActivity.java @@ -1,15 +1,11 @@ package org.meowcat.edxposed.manager; -import android.content.ActivityNotFoundException; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Bundle; -import android.os.Environment; -import android.os.Handler; import android.text.TextUtils; -import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -24,7 +20,6 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.Toolbar; -import androidx.core.view.MenuItemCompat; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -43,15 +38,11 @@ import org.meowcat.edxposed.manager.util.NavUtil; import org.meowcat.edxposed.manager.util.RepoLoader; import java.io.BufferedReader; -import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.FileWriter; -import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.OutputStream; import java.io.PrintWriter; import java.text.DateFormat; import java.text.SimpleDateFormat; @@ -77,7 +68,6 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi private DateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); private ModuleUtil mModuleUtil; private ModuleAdapter mAdapter = null; - private MenuItem mClickedMenuItem = null; private RecyclerView mListView; private SwipeRefreshLayout mSwipeRefreshLayout; private Runnable reloadModules = new Runnable() { @@ -162,152 +152,140 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_modules, menu); - mSearchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.menu_search)); + mSearchView = (SearchView) menu.findItem(R.id.menu_search).getActionView(); mSearchView.setOnQueryTextListener(mSearchListener); return super.onCreateOptionsMenu(menu); } @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, - grantResults); - if (requestCode == XposedApp.WRITE_EXTERNAL_PERMISSION) { - if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { - if (mClickedMenuItem != null) { - new Handler().postDelayed(() -> onOptionsItemSelected(mClickedMenuItem), 500); + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (resultCode != RESULT_OK) { + return; + } + if (requestCode == 42) { + File listModules = new File(XposedApp.ENABLED_MODULES_LIST_FILE); + if (data != null) { + Uri uri = data.getData(); + if (uri != null) { + try { + OutputStream os = getContentResolver().openOutputStream(uri); + if (os != null) { + FileInputStream in = new FileInputStream(listModules); + byte[] buffer = new byte[1024]; + int len; + while ((len = in.read(buffer)) > 0) { + os.write(buffer, 0, len); + } + os.close(); + } + } catch (Exception e) { + Snackbar.make(findViewById(R.id.snackbar), getResources().getString(R.string.logs_save_failed) + "\n" + e.getMessage(), Snackbar.LENGTH_LONG).show(); + } + } + } + } else if (requestCode == 43) { + if (data != null) { + Uri uri = data.getData(); + if (uri != null) { + try { + OutputStream os = getContentResolver().openOutputStream(uri); + if (os != null) { + PrintWriter fileOut = new PrintWriter(os); + + Set keys = ModuleUtil.getInstance().getModules().keySet(); + for (String key1 : keys) { + fileOut.println(key1); + } + fileOut.close(); + os.close(); + } + } catch (Exception e) { + Snackbar.make(findViewById(R.id.snackbar), getResources().getString(R.string.logs_save_failed) + "\n" + e.getMessage(), Snackbar.LENGTH_LONG).show(); + } + } + } + } else if (requestCode == 44) { + if (data != null) { + Uri uri = data.getData(); + if (uri != null) { + try { + importModules(uri); + } catch (Exception e) { + e.printStackTrace(); + } } - } else { - Snackbar.make(findViewById(R.id.snackbar), R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show(); } } } @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { - File enabledModulesPath = new File(XposedApp.createFolder(), "enabled_modules.list"); - File installedModulesPath = new File(XposedApp.createFolder(), "installed_modules.list"); - File listModules = new File(XposedApp.ENABLED_MODULES_LIST_FILE); - - mClickedMenuItem = item; - - if (checkPermissions()) - return false; - + Intent intent; switch (item.getItemId()) { case R.id.export_enabled_modules: - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - return false; - } - if (ModuleUtil.getInstance().getEnabledModules().isEmpty()) { Snackbar.make(findViewById(R.id.snackbar), R.string.no_enabled_modules, Snackbar.LENGTH_SHORT).show(); return false; } - - try { - XposedApp.createFolder(); - - FileInputStream in = new FileInputStream(listModules); - FileOutputStream out = new FileOutputStream(enabledModulesPath); - - byte[] buffer = new byte[1024]; - int len; - while ((len = in.read(buffer)) > 0) { - out.write(buffer, 0, len); - } - in.close(); - out.close(); - } catch (IOException e) { - Snackbar.make(findViewById(R.id.snackbar), getResources().getString(R.string.logs_save_failed) + "\n" + e.getMessage(), Snackbar.LENGTH_LONG).show(); - return false; - } - - Snackbar.make(findViewById(R.id.snackbar), enabledModulesPath.toString(), Snackbar.LENGTH_LONG).show(); + intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("text/*"); + intent.putExtra(Intent.EXTRA_TITLE, "enabled_modules.list"); + startActivityForResult(intent, 42); return true; case R.id.export_installed_modules: - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - Snackbar.make(findViewById(R.id.snackbar), R.string.sdcard_not_writable, Snackbar.LENGTH_LONG).show(); - return false; - } Map installedModules = ModuleUtil.getInstance().getModules(); if (installedModules.isEmpty()) { Snackbar.make(findViewById(R.id.snackbar), R.string.no_installed_modules, Snackbar.LENGTH_SHORT).show(); return false; } - - try { - XposedApp.createFolder(); - - FileWriter fw = new FileWriter(installedModulesPath); - BufferedWriter bw = new BufferedWriter(fw); - PrintWriter fileOut = new PrintWriter(bw); - - Set keys = installedModules.keySet(); - for (String key1 : keys) { - fileOut.println(key1); - } - - fileOut.close(); - } catch (IOException e) { - Snackbar.make(findViewById(R.id.snackbar), getResources().getString(R.string.logs_save_failed) + "\n" + e.getMessage(), Snackbar.LENGTH_LONG).show(); - return false; - } - - Snackbar.make(findViewById(R.id.snackbar), installedModulesPath.toString(), Snackbar.LENGTH_LONG).show(); + intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("text/*"); + intent.putExtra(Intent.EXTRA_TITLE, "installed_modules.list"); + startActivityForResult(intent, 43); return true; case R.id.import_installed_modules: - return importModules(installedModulesPath); case R.id.import_enabled_modules: - return importModules(enabledModulesPath); + intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); + startActivityForResult(intent, 44); + return true; } return super.onOptionsItemSelected(item); } - private boolean importModules(File path) { - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - Snackbar.make(findViewById(R.id.snackbar), R.string.sdcard_not_writable, Snackbar.LENGTH_LONG).show(); - return false; - } - InputStream ips = null; + private void importModules(Uri uri) { RepoLoader repoLoader = RepoLoader.getInstance(); List list = new ArrayList<>(); - if (!path.exists()) { - Snackbar.make(findViewById(R.id.snackbar), R.string.no_backup_found, Snackbar.LENGTH_LONG).show(); - return false; - } - try { - ips = new FileInputStream(path); - } catch (FileNotFoundException e) { - Log.e(XposedApp.TAG, "ModulesFragment -> " + e.getMessage()); - } - - if (path.length() == 0) { - Snackbar.make(findViewById(R.id.snackbar), R.string.file_is_empty, Snackbar.LENGTH_LONG).show(); - return false; - } try { - assert ips != null; - InputStreamReader ipsr = new InputStreamReader(ips); - BufferedReader br = new BufferedReader(ipsr); + InputStream inputStream = getContentResolver().openInputStream(uri); + InputStreamReader isr = new InputStreamReader(inputStream); + BufferedReader br = new BufferedReader(isr); String line; while ((line = br.readLine()) != null) { Module m = repoLoader.getModule(line); if (m == null) { - Snackbar.make(findViewById(R.id.snackbar), getString(R.string.download_details_not_found, - line), Snackbar.LENGTH_SHORT).show(); + Snackbar.make(findViewById(R.id.snackbar), getString(R.string.download_details_not_found, line), Snackbar.LENGTH_SHORT).show(); } else { list.add(m); } } br.close(); - } catch (ActivityNotFoundException | IOException e) { - Snackbar.make(findViewById(R.id.snackbar), e.toString(), Snackbar.LENGTH_SHORT).show(); + } catch (Exception e) { + e.printStackTrace(); + Snackbar.make(findViewById(R.id.snackbar), e.getLocalizedMessage(), Snackbar.LENGTH_SHORT).show(); } for (final Module m : list) { + if (mModuleUtil.getModule(m.packageName) != null) { + continue; + } ModuleVersion mv = null; for (int i = 0; i < m.versions.size(); i++) { ModuleVersion mvTemp = m.versions.get(i); @@ -319,13 +297,11 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi } if (mv != null) { - DownloadsUtil.addModule(this, m.name, mv.downloadLink, false, (context, info) -> new InstallApkUtil(this, info).execute()); + DownloadsUtil.addModule(this, m.name, mv.downloadLink, (context, info) -> new InstallApkUtil(this, info).execute()); } } ModuleUtil.getInstance().reloadInstalledModules(); - - return true; } @Override diff --git a/app/src/main/java/org/meowcat/edxposed/manager/StatusInstallerFragment.java b/app/src/main/java/org/meowcat/edxposed/manager/StatusInstallerFragment.java index edf1959d..3dcddcca 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/StatusInstallerFragment.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/StatusInstallerFragment.java @@ -3,13 +3,9 @@ package org.meowcat.edxposed.manager; import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.os.FileUtils; import android.text.Html; import android.util.Log; import android.view.LayoutInflater; @@ -28,13 +24,10 @@ import java.io.File; import java.io.FileReader; import java.io.IOException; import java.lang.reflect.Method; -import java.util.List; -import java.util.Objects; @SuppressLint("StaticFieldLeak") public class StatusInstallerFragment extends Fragment { - public static final File DISABLE_FILE = new File(XposedApp.BASE_DIR + "conf/disabled"); private static AppCompatActivity sActivity; private static String mUpdateLink; private static View mUpdateView; @@ -98,20 +91,6 @@ public class StatusInstallerFragment extends Fragment { } } - @SuppressWarnings("SameParameterValue") - @SuppressLint({"WorldReadableFiles", "WorldWriteableFiles"}) - private static void setFilePermissionsFromMode(String name, int mode) { - int perms = FileUtils.S_IRUSR | FileUtils.S_IWUSR - | FileUtils.S_IRGRP | FileUtils.S_IWGRP; - if ((mode & Context.MODE_WORLD_READABLE) != 0) { - perms |= FileUtils.S_IROTH; - } - if ((mode & Context.MODE_WORLD_WRITEABLE) != 0) { - perms |= FileUtils.S_IWOTH; - } - FileUtils.setPermissions(name, perms, -1, -1); - } - @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -119,7 +98,6 @@ public class StatusInstallerFragment extends Fragment { } @SuppressLint("WorldReadableFiles") - @SuppressWarnings("ResultOfMethodCallIgnored") @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View v = inflater.inflate(R.layout.status_installer, container, false); @@ -156,8 +134,6 @@ public class StatusInstallerFragment extends Fragment { cpu.setText(getCompleteArch()); determineVerifiedBootState(v); - - refreshKnownIssue(); return v; } @@ -191,57 +167,58 @@ public class StatusInstallerFragment extends Fragment { } } - @SuppressWarnings("SameParameterValue") - private boolean checkAppInstalled(Context context, String pkgName) { - if (pkgName == null || pkgName.isEmpty()) { - return false; - } - final PackageManager packageManager = context.getPackageManager(); - List info = packageManager.getInstalledPackages(0); - if (info == null || info.isEmpty()) { - return false; - } - for (int i = 0; i < info.size(); i++) { - if (pkgName.equals(info.get(i).packageName)) { - return true; + /* + @SuppressWarnings("SameParameterValue") + private boolean checkAppInstalled(Context context, String pkgName) { + if (pkgName == null || pkgName.isEmpty()) { + return false; } - } - return false; - } - - @SuppressLint("StringFormatInvalid") - private void refreshKnownIssue() { - String issueName = null; - String issueLink = null; - final ApplicationInfo appInfo = Objects.requireNonNull(getActivity()).getApplicationInfo(); - final File baseDir = new File(XposedApp.BASE_DIR); - final File baseDirCanonical = getCanonicalFile(baseDir); - final File baseDirActual = new File(Build.VERSION.SDK_INT >= 24 ? appInfo.deviceProtectedDataDir : appInfo.dataDir); - final File baseDirActualCanonical = getCanonicalFile(baseDirActual); - - if (new File("/system/framework/core.jar.jex").exists()) { - issueName = "Aliyun OS"; - issueLink = "https://forum.xda-developers.com/showpost.php?p=52289793&postcount=5"; -// } else if (Build.VERSION.SDK_INT < 24 && (new File("/data/miui/DexspyInstaller.jar").exists() || checkClassExists("miui.dexspy.DexspyInstaller"))) { -// issueName = "MIUI/Dexspy"; -// issueLink = "https://forum.xda-developers.com/showpost.php?p=52291098&postcount=6"; -// } else if (Build.VERSION.SDK_INT < 24 && new File("/system/framework/twframework.jar").exists()) { -// issueName = "Samsung TouchWiz ROM"; -// issueLink = "https://forum.xda-developers.com/showthread.php?t=3034811"; - } else if (!baseDirCanonical.equals(baseDirActualCanonical)) { - Log.e(XposedApp.TAG, "Base directory: " + getPathWithCanonicalPath(baseDir, baseDirCanonical)); - Log.e(XposedApp.TAG, "Expected: " + getPathWithCanonicalPath(baseDirActual, baseDirActualCanonical)); - issueName = getString(R.string.known_issue_wrong_base_directory, getPathWithCanonicalPath(baseDirActual, baseDirActualCanonical)); - } else if (!baseDir.exists()) { - issueName = getString(R.string.known_issue_missing_base_directory); - issueLink = "https://github.com/rovo89/XposedInstaller/issues/393"; - } else if (checkAppInstalled(getContext(), "com.solohsu.android.edxp.manager")) { - issueName = getString(R.string.edxp_installer_installed); - issueLink = getString(R.string.about_support); + final PackageManager packageManager = context.getPackageManager(); + List info = packageManager.getInstalledPackages(0); + if (info == null || info.isEmpty()) { + return false; + } + for (int i = 0; i < info.size(); i++) { + if (pkgName.equals(info.get(i).packageName)) { + return true; + } + } + return false; } - } + @SuppressLint("StringFormatInvalid") + private void refreshKnownIssue() { + String issueName = null; + String issueLink = null; + final ApplicationInfo appInfo = Objects.requireNonNull(getActivity()).getApplicationInfo(); + final File baseDir = new File(XposedApp.BASE_DIR); + final File baseDirCanonical = getCanonicalFile(baseDir); + final File baseDirActual = new File(Build.VERSION.SDK_INT >= 24 ? appInfo.deviceProtectedDataDir : appInfo.dataDir); + final File baseDirActualCanonical = getCanonicalFile(baseDirActual); + if (new File("/system/framework/core.jar.jex").exists()) { + issueName = "Aliyun OS"; + issueLink = "https://forum.xda-developers.com/showpost.php?p=52289793&postcount=5"; + // } else if (Build.VERSION.SDK_INT < 24 && (new File("/data/miui/DexspyInstaller.jar").exists() || checkClassExists("miui.dexspy.DexspyInstaller"))) { + // issueName = "MIUI/Dexspy"; + // issueLink = "https://forum.xda-developers.com/showpost.php?p=52291098&postcount=6"; + // } else if (Build.VERSION.SDK_INT < 24 && new File("/system/framework/twframework.jar").exists()) { + // issueName = "Samsung TouchWiz ROM"; + // issueLink = "https://forum.xda-developers.com/showthread.php?t=3034811"; + } else if (!baseDirCanonical.equals(baseDirActualCanonical)) { + Log.e(XposedApp.TAG, "Base directory: " + getPathWithCanonicalPath(baseDir, baseDirCanonical)); + Log.e(XposedApp.TAG, "Expected: " + getPathWithCanonicalPath(baseDirActual, baseDirActualCanonical)); + issueName = getString(R.string.known_issue_wrong_base_directory, getPathWithCanonicalPath(baseDirActual, baseDirActualCanonical)); + } else if (!baseDir.exists()) { + issueName = getString(R.string.known_issue_missing_base_directory); + issueLink = "https://github.com/rovo89/XposedInstaller/issues/393"; + } else if (checkAppInstalled(getContext(), "com.solohsu.android.edxp.manager")) { + issueName = getString(R.string.edxp_installer_installed); + issueLink = getString(R.string.about_support); + } + + } + */ private String getAndroidVersion() { switch (Build.VERSION.SDK_INT) { // case 16: @@ -296,23 +273,24 @@ public class StatusInstallerFragment extends Fragment { return manufacturer; } - private File getCanonicalFile(File file) { - try { - return file.getCanonicalFile(); - } catch (IOException e) { - Log.e(XposedApp.TAG, "Failed to get canonical file for " + file.getAbsolutePath(), e); - return file; + /* + private File getCanonicalFile(File file) { + try { + return file.getCanonicalFile(); + } catch (IOException e) { + Log.e(XposedApp.TAG, "Failed to get canonical file for " + file.getAbsolutePath(), e); + return file; + } } - } - private String getPathWithCanonicalPath(File file, File canonical) { - if (file.equals(canonical)) { - return file.getAbsolutePath(); - } else { - return file.getAbsolutePath() + " \u2192 " + canonical.getAbsolutePath(); + private String getPathWithCanonicalPath(File file, File canonical) { + if (file.equals(canonical)) { + return file.getAbsolutePath(); + } else { + return file.getAbsolutePath() + " \u2192 " + canonical.getAbsolutePath(); + } } - } - + */ private int extractIntPart(String str) { int result = 0, length = str.length(); for (int offset = 0; offset < length; offset++) { diff --git a/app/src/main/java/org/meowcat/edxposed/manager/XposedApp.java b/app/src/main/java/org/meowcat/edxposed/manager/XposedApp.java index 8adff14c..82ff254f 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/XposedApp.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/XposedApp.java @@ -105,31 +105,35 @@ public class XposedApp extends de.robv.android.xposed.installer.XposedApp implem public void onCreate() { super.onCreate(); - try { - Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> { + if (!BuildConfig.DEBUG) { + try { + Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - throwable.printStackTrace(pw); - String stackTraceString = sw.toString(); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + throwable.printStackTrace(pw); + String stackTraceString = sw.toString(); - //Reduce data to 128KB so we don't get a TransactionTooLargeException when sending the intent. - //The limit is 1MB on Android but some devices seem to have it lower. - //See: http://developer.android.com/reference/android/os/TransactionTooLargeException.html - //And: http://stackoverflow.com/questions/11451393/what-to-do-on-transactiontoolargeexception#comment46697371_12809171 - if (stackTraceString.length() > 131071) { - String disclaimer = " [stack trace too large]"; - stackTraceString = stackTraceString.substring(0, 131071 - disclaimer.length()) + disclaimer; - } - Intent intent = new Intent(XposedApp.this, CrashReportActivity.class); - intent.putExtra(BuildConfig.APPLICATION_ID + ".EXTRA_STACK_TRACE", stackTraceString); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - XposedApp.this.startActivity(intent); - android.os.Process.killProcess(android.os.Process.myPid()); - System.exit(10); - }); - } catch (Throwable t) { + //Reduce data to 128KB so we don't get a TransactionTooLargeException when sending the intent. + //The limit is 1MB on Android but some devices seem to have it lower. + //See: http://developer.android.com/reference/android/os/TransactionTooLargeException.html + //And: http://stackoverflow.com/questions/11451393/what-to-do-on-transactiontoolargeexception#comment46697371_12809171 + if (stackTraceString.length() > 131071) { + String disclaimer = " [stack trace too large]"; + stackTraceString = stackTraceString.substring(0, 131071 - disclaimer.length()) + disclaimer; + } + Intent intent = new Intent(XposedApp.this, CrashReportActivity.class); + intent.putExtra(BuildConfig.APPLICATION_ID + ".EXTRA_STACK_TRACE", stackTraceString); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + XposedApp.this.startActivity(intent); + android.os.Process.killProcess(android.os.Process.myPid()); + System.exit(10); + }); + } catch (Throwable t) { + t.printStackTrace(); + } } + mInstance = this; mUiThread = Thread.currentThread(); mMainHandler = new Handler(); diff --git a/app/src/main/java/org/meowcat/edxposed/manager/util/DownloadsUtil.java b/app/src/main/java/org/meowcat/edxposed/manager/util/DownloadsUtil.java index 12a83ee0..5cd6e744 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/util/DownloadsUtil.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/util/DownloadsUtil.java @@ -4,26 +4,17 @@ import android.annotation.SuppressLint; import android.app.DownloadManager; import android.app.DownloadManager.Query; import android.app.DownloadManager.Request; -import android.app.ProgressDialog; import android.content.Context; -import android.content.DialogInterface; import android.content.SharedPreferences; import android.database.Cursor; import android.net.Uri; -import android.os.Environment; import android.provider.MediaStore; -import android.util.Log; import android.widget.Toast; import androidx.annotation.NonNull; -import androidx.core.content.ContextCompat; -import androidx.core.os.EnvironmentCompat; import org.meowcat.edxposed.manager.R; import org.meowcat.edxposed.manager.XposedApp; -import org.meowcat.edxposed.manager.repo.Module; -import org.meowcat.edxposed.manager.repo.ModuleVersion; -import org.meowcat.edxposed.manager.repo.ReleaseType; import java.io.File; import java.io.FileOutputStream; @@ -40,7 +31,7 @@ import java.util.Map; import java.util.Objects; public class DownloadsUtil { - static final String MIME_TYPE_APK = "application/vnd.android.package-archive"; + public static final String MIME_TYPE_APK = "application/vnd.android.package-archive"; //private static final String MIME_TYPE_ZIP = "application/zip"; private static final Map mCallbacks = new HashMap<>(); @SuppressLint("StaticFieldLeak") @@ -48,152 +39,55 @@ public class DownloadsUtil { private static final SharedPreferences mPref = mApp .getSharedPreferences("download_cache", Context.MODE_PRIVATE); - private static String DOWNLOAD_MODULES = "modules"; - private static DownloadInfo add(Builder b) { Context context = b.mContext; removeAllForUrl(context, b.mUrl); - if (!b.mDialog) { - synchronized (mCallbacks) { - mCallbacks.put(b.mUrl, b.mCallback); - } - } - - String savePath = "Download/EdXposedManager"; - if (b.mModule) { - savePath += "/modules"; + synchronized (mCallbacks) { + mCallbacks.put(b.mUrl, b.mCallback); } Request request = new Request(Uri.parse(b.mUrl)); request.setTitle(b.mTitle); request.setMimeType(b.mMimeType.toString()); - if (b.mSave) { + /*if (b.mSave) { try { request.setDestinationInExternalPublicDir(savePath, b.mTitle + b.mMimeType.getExtension()); } catch (IllegalStateException e) { Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show(); } - } else if (b.mDestination != null) { - //noinspection ResultOfMethodCallIgnored - b.mDestination.getParentFile().mkdirs(); - removeAllForLocalFile(context, b.mDestination); - request.setDestinationUri(Uri.fromFile(b.mDestination)); - } - + } else */ + File destination = new File(context.getExternalCacheDir(), "/downloads/" + b.mTitle + b.mMimeType.getExtension()); + request.setDestinationUri(Uri.fromFile(destination)); request.setNotificationVisibility(Request.VISIBILITY_VISIBLE); - DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); long id = dm.enqueue(request); - if (b.mDialog) { - showDownloadDialog(b, id); - } - return getById(context, id); } - private static File[] getDownloadDirs(String subDir) { - Context context = XposedApp.getInstance(); - ArrayList dirs = new ArrayList<>(2); - for (File dir : ContextCompat.getExternalCacheDirs(context)) { - if (dir != null && EnvironmentCompat.getStorageState(dir).equals(Environment.MEDIA_MOUNTED)) { - dirs.add(new File(new File(dir, "downloads"), subDir)); - } - } - dirs.add(new File(new File(context.getCacheDir(), "downloads"), subDir)); - return dirs.toArray(new File[0]); - } - - private static File getDownloadTarget(String subDir, String filename) { - return new File(getDownloadDirs(subDir)[0], filename); - } - - private static File getDownloadTargetForUrl(String subDir, String url) { - return getDownloadTarget(subDir, Uri.parse(url).getLastPathSegment()); - } - - public static DownloadInfo addModule(Context context, String title, String url, boolean save, DownloadFinishedCallback callback) { + public static DownloadInfo addModule(Context context, String title, String url, DownloadFinishedCallback callback) { return new Builder(context) .setTitle(title) .setUrl(url) - .setDestinationFromUrl(DownloadsUtil.DOWNLOAD_MODULES) .setCallback(callback) - .setSave(save) .setModule(true) .setMimeType(MIME_TYPES.APK) .download(); } - private static void showDownloadDialog(final Builder b, final long id) { - final Context context = b.mContext; - final ProgressDialog dialog = new ProgressDialog(context); - dialog.setTitle(b.mTitle); - dialog.setMessage(context.getString(R.string.download_view_waiting)); - dialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(R.string.download_view_cancel), (dialog1, which) -> dialog1.cancel()); - dialog.setOnCancelListener(dialog12 -> removeById(context, id)); + /* + public static ModuleVersion getStableVersion(Module m) { + for (int i = 0; i < m.versions.size(); i++) { + ModuleVersion mvTemp = m.versions.get(i); - dialog.setProgress(0); - dialog.setCanceledOnTouchOutside(false); - dialog.setProgressNumberFormat(context.getString(R.string.download_progress)); - dialog.show(); - - new Thread("DownloadDialog") { - @Override - public void run() { - while (true) { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - return; - } - - final DownloadInfo info = getById(context, id); - if (info == null) { - dialog.cancel(); - return; - } else if (info.status == DownloadManager.STATUS_FAILED) { - dialog.cancel(); - XposedApp.runOnUiThread(() -> Toast.makeText(context, - context.getString(R.string.download_view_failed, info.reason), - Toast.LENGTH_LONG).show()); - return; - } else if (info.status == DownloadManager.STATUS_SUCCESSFUL) { - dialog.dismiss(); - // Hack to reset stat information. - //noinspection ResultOfMethodCallIgnored - new File(info.localFilename).setExecutable(false); - if (b.mCallback != null) { - b.mCallback.onDownloadFinished(context, info); - } - return; - } - - XposedApp.runOnUiThread(() -> { - if (info.totalSize <= 0 || info.status != DownloadManager.STATUS_RUNNING) { - dialog.setMessage(context.getString(R.string.download_view_waiting)); - } else { - dialog.setMessage(context.getString(R.string.download_running)); - dialog.setProgress(info.bytesDownloaded / 1024); - dialog.setMax(info.totalSize / 1024); - } - }); + if (mvTemp.relType == ReleaseType.STABLE) { + return mvTemp; } } - }.start(); - } - - public static ModuleVersion getStableVersion(Module m) { - for (int i = 0; i < m.versions.size(); i++) { - ModuleVersion mvTemp = m.versions.get(i); - - if (mvTemp.relType == ReleaseType.STABLE) { - return mvTemp; - } + return null; } - return null; - } - + */ public static DownloadInfo getById(Context context, long id) { DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); Cursor c = dm.query(new Query().setFilterById(id)); @@ -317,7 +211,7 @@ public class DownloadsUtil { dm.remove(ids); } - +/* private static void removeAllForLocalFile(Context context, File file) { //noinspection ResultOfMethodCallIgnored file.delete(); @@ -367,7 +261,7 @@ public class DownloadsUtil { ids[i] = idsList.get(i); dm.remove(ids); - } + }*/ // public static void removeOutdated(Context context, long cutoff) { // DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); @@ -563,9 +457,6 @@ public class DownloadsUtil { private String mUrl = null; private DownloadFinishedCallback mCallback = null; private MIME_TYPES mMimeType = MIME_TYPES.APK; - private File mDestination = null; - private boolean mDialog = false; - private boolean mSave = false; public Builder(Context context) { mContext = context; @@ -591,33 +482,11 @@ public class DownloadsUtil { return this; } - Builder setDestination(File file) { - mDestination = file; - return this; - } - - Builder setDestinationFromUrl(String subDir) { - if (mUrl == null) { - throw new IllegalStateException("URL must be set first"); - } - return setDestination(getDownloadTargetForUrl(subDir, mUrl)); - } - - public Builder setSave(boolean save) { - this.mSave = save; - return this; - } - public Builder setModule(boolean module) { this.mModule = module; return this; } - public Builder setDialog(boolean dialog) { - mDialog = dialog; - return this; - } - public DownloadInfo download() { return add(this); } diff --git a/app/src/main/java/org/meowcat/edxposed/manager/widget/DownloadView.java b/app/src/main/java/org/meowcat/edxposed/manager/widget/DownloadView.java index 142a10e4..c3cdb76b 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/widget/DownloadView.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/widget/DownloadView.java @@ -1,10 +1,8 @@ package org.meowcat.edxposed.manager.widget; -import android.Manifest; -import android.annotation.SuppressLint; import android.app.DownloadManager; import android.content.Context; -import android.content.pm.PackageManager; +import android.content.Intent; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; @@ -12,9 +10,7 @@ import android.widget.Button; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; -import android.widget.Toast; -import androidx.core.app.ActivityCompat; import androidx.fragment.app.Fragment; import org.meowcat.edxposed.manager.R; @@ -23,20 +19,15 @@ import org.meowcat.edxposed.manager.util.DownloadsUtil.DownloadFinishedCallback; import java.util.Objects; -import static org.meowcat.edxposed.manager.XposedApp.WRITE_EXTERNAL_PERMISSION; - public class DownloadView extends LinearLayout { - @SuppressLint("StaticFieldLeak") - public static Button mClickedButton; + public static DownloadsUtil.DownloadInfo mInfo = null; private final Button btnDownload; private final Button btnDownloadCancel; private final Button btnInstall; - private final Button btnRemove; private final Button btnSave; private final ProgressBar progressBar; private final TextView txtInfo; public Fragment fragment; - private DownloadsUtil.DownloadInfo mInfo = null; private String mUrl = null; private final Runnable refreshViewRunnable = new Runnable() { @Override @@ -45,7 +36,6 @@ public class DownloadView extends LinearLayout { btnDownload.setVisibility(View.GONE); btnSave.setVisibility(View.GONE); btnDownloadCancel.setVisibility(View.GONE); - btnRemove.setVisibility(View.GONE); btnInstall.setVisibility(View.GONE); progressBar.setVisibility(View.GONE); txtInfo.setVisibility(View.VISIBLE); @@ -54,7 +44,6 @@ public class DownloadView extends LinearLayout { btnDownload.setVisibility(View.VISIBLE); btnSave.setVisibility(View.VISIBLE); btnDownloadCancel.setVisibility(View.GONE); - btnRemove.setVisibility(View.GONE); btnInstall.setVisibility(View.GONE); progressBar.setVisibility(View.GONE); txtInfo.setVisibility(View.GONE); @@ -66,7 +55,6 @@ public class DownloadView extends LinearLayout { btnDownload.setVisibility(View.GONE); btnSave.setVisibility(View.GONE); btnDownloadCancel.setVisibility(View.VISIBLE); - btnRemove.setVisibility(View.GONE); btnInstall.setVisibility(View.GONE); progressBar.setVisibility(View.VISIBLE); txtInfo.setVisibility(View.VISIBLE); @@ -88,7 +76,6 @@ public class DownloadView extends LinearLayout { btnDownload.setVisibility(View.VISIBLE); btnSave.setVisibility(View.VISIBLE); btnDownloadCancel.setVisibility(View.GONE); - btnRemove.setVisibility(View.GONE); btnInstall.setVisibility(View.GONE); progressBar.setVisibility(View.GONE); txtInfo.setVisibility(View.VISIBLE); @@ -98,9 +85,8 @@ public class DownloadView extends LinearLayout { case DownloadManager.STATUS_SUCCESSFUL: btnDownload.setVisibility(View.GONE); - btnSave.setVisibility(View.GONE); + btnSave.setVisibility(View.VISIBLE); btnDownloadCancel.setVisibility(View.GONE); - btnRemove.setVisibility(View.VISIBLE); btnInstall.setVisibility(View.VISIBLE); progressBar.setVisibility(View.GONE); txtInfo.setVisibility(View.VISIBLE); @@ -123,27 +109,27 @@ public class DownloadView extends LinearLayout { btnDownload = findViewById(R.id.btnDownload); btnDownloadCancel = findViewById(R.id.btnDownloadCancel); - btnRemove = findViewById(R.id.btnRemove); btnInstall = findViewById(R.id.btnInstall); btnSave = findViewById(R.id.save); btnDownload.setOnClickListener(v -> { - mClickedButton = btnDownload; - - mInfo = DownloadsUtil.addModule(getContext(), mTitle, mUrl, false, mCallback); + mInfo = DownloadsUtil.addModule(getContext(), mTitle, mUrl, mCallback); refreshViewFromUiThread(); - if (mInfo != null) new DownloadMonitor().start(); }); btnSave.setOnClickListener(v -> { - mClickedButton = btnSave; - - if (checkPermissions()) - return; - - mInfo = DownloadsUtil.addModule(getContext(), mTitle, mUrl, true, (context1, info) -> Toast.makeText(context1, context1.getString(R.string.module_saved, info.localFilename), Toast.LENGTH_SHORT).show()); + mInfo = DownloadsUtil.addModule(getContext(), mTitle, mUrl, (context1, info) -> { + Intent exportIntent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + exportIntent.addCategory(Intent.CATEGORY_OPENABLE); + exportIntent.setType(DownloadsUtil.MIME_TYPE_APK); + exportIntent.putExtra(Intent.EXTRA_TITLE, mTitle + ".apk"); + fragment.startActivityForResult(exportIntent, 42); + }); + refreshViewFromUiThread(); + if (mInfo != null) + new DownloadMonitor().start(); }); btnDownloadCancel.setOnClickListener(v -> { @@ -154,14 +140,6 @@ public class DownloadView extends LinearLayout { // UI update will happen automatically by the DownloadMonitor }); - btnRemove.setOnClickListener(v -> { - if (mInfo == null) - return; - - DownloadsUtil.removeById(getContext(), mInfo.id); - // UI update will happen automatically by the DownloadMonitor - }); - btnInstall.setOnClickListener(v -> { if (mCallback == null) return; @@ -175,15 +153,6 @@ public class DownloadView extends LinearLayout { refreshViewFromUiThread(); } - private boolean checkPermissions() { - if (ActivityCompat.checkSelfPermission(this.getContext(), - Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - fragment.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, WRITE_EXTERNAL_PERMISSION); - return true; - } - return false; - } - private void refreshViewFromUiThread() { refreshViewRunnable.run(); } diff --git a/app/src/main/res/layout/download_view.xml b/app/src/main/res/layout/download_view.xml index e50bf8ef..163b5d6f 100644 --- a/app/src/main/res/layout/download_view.xml +++ b/app/src/main/res/layout/download_view.xml @@ -42,16 +42,6 @@ android:visibility="gone" tools:ignore="ButtonOrder" /> - - 无法将日志写入 SD 卡: - 无法找到 SD 卡或不可写入 立即清理日志 日志清理成功 无法清理日志: @@ -156,7 +155,6 @@ 无法读取下载的文件: %s 下载的文件并非有效的 APK (或存在兼容性问题) 文件包名不正确(下载: %1$s, 预期: %2$s) - 正在下载 下载 %1$s 失败: %2$d(%3$s) @@ -173,7 +171,6 @@ 没有任何已安装的模块 启用浮动通知 对新安装或已更新的模块启用浮动通知 - 找不到备份 没有任何已启用的模块 分享 @@ -195,7 +192,6 @@ 更新日志 滚动到顶部 - 文件为空 静默安装(root) 勾选后在 EdXposed Manager 内安装模块时将不再显示安装提示 模块已保存至 %1$s diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 8caefe74..01e3441c 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -126,7 +126,6 @@ 無法將日誌寫入 SD 卡: - 無法找到 SD 卡或不可寫入 立即清理日誌 日誌清理成功 無法清理日誌: @@ -156,7 +155,6 @@ 無法讀取下載的文件: %s 下載的文件並非有效的 APK (或存在兼容性問題) 文件包名不正確(下載: %1$s, 預期: %2$s) - 正在下載 下載 %1$s 失敗: %2$d(%3$s) @@ -173,7 +171,6 @@ 沒有任何已安裝的模塊 啟用浮動通知 對新安裝或已更新的模塊啟用浮動通知 - 找不到備份 沒有任何已啟用的模塊 分享 @@ -195,7 +192,6 @@ 更新日誌 滾動到頂部 - 文件為空 靜默安裝(root) 勾選後在 EdXposed Manager 內安裝模塊時將不再顯示安裝提示 模塊已保存至 %1$s diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 49be36d8..7b40bbb2 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -126,7 +126,6 @@ 無法將日誌寫入 SD 卡: - 無法找到 SD 卡或不可寫入 立即清理日誌 日誌清理成功 無法清理日誌: @@ -156,7 +155,6 @@ 無法讀取下載的檔案: %s 下載的檔案並非有效的 APK (或存在相容性問題) 檔案包名不正確(下載: %1$s, 預期: %2$s) - 正在下載 下載 %1$s 失敗: %2$d(%3$s) @@ -173,7 +171,6 @@ 沒有任何已安裝的模組 啟用浮動通知 對新安裝或已更新的模組啟用浮動通知 - 找不到備份 沒有任何已啟用的模組 分享 @@ -195,7 +192,6 @@ 更新日誌 滾動到頂部 - 檔案為空 靜默安裝(root) 勾選後在 EdXposed Manager 內安裝模組時將不再顯示安裝提示 模組已儲存至 %1$s diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 47afed7f..e8a69644 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -128,7 +128,6 @@ Could not write log to SD card: - SD card not found or not writable Clear log now Log successfully cleared. Could not clear the log: @@ -158,8 +157,6 @@ Could not read downloaded file: %s Downloaded file is not a valid APK (or incompatible) Package name is incorrect (downloaded: %1$s, expected: %2$s) - Download is running - %1$,d / %2$,d kB Downloading %1$s failed: %2$d (%3$s) @@ -176,7 +173,6 @@ There are no installed modules! Enable Heads-Up notification This option enables heads up notification on new/updated module - No backup found There are no modules enabled Share with… @@ -198,7 +194,6 @@ Changelog Scroll to top - File is empty Silent installation (root) If checked EdXposed Manager will install modules without a prompt Module saved at %1$s diff --git a/app/src/main/res/xml/file_paths.xml b/app/src/main/res/xml/file_paths.xml index a132791b..1f1be923 100644 --- a/app/src/main/res/xml/file_paths.xml +++ b/app/src/main/res/xml/file_paths.xml @@ -2,10 +2,7 @@ - + path="/downloads" />