Remove storage permission
This commit is contained in:
parent
d2914f947d
commit
ed8836ca6d
|
|
@ -4,8 +4,6 @@
|
||||||
package="org.meowcat.edxposed.manager">
|
package="org.meowcat.edxposed.manager">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
|
|
@ -98,7 +96,7 @@
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="org.meowcat.edxposed.manager.fileprovider"
|
android:authorities="${applicationId}.fileprovider"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
<meta-data
|
<meta-data
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ public class XposedApp extends Application {
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
mInstance = this;
|
mInstance = this;
|
||||||
|
reloadXposedProp();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reloadXposedProp() {
|
public void reloadXposedProp() {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,10 @@
|
||||||
package org.meowcat.edxposed.manager;
|
package org.meowcat.edxposed.manager;
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
@ -20,7 +17,6 @@ import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.StyleRes;
|
import androidx.annotation.StyleRes;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.appcompat.app.AppCompatDelegate;
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
import androidx.core.app.ActivityCompat;
|
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
|
|
||||||
|
|
@ -152,16 +148,6 @@ public class BaseActivity extends AppCompatActivity {
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean checkPermissions() {
|
|
||||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, XposedApp.WRITE_EXTERNAL_PERMISSION);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reboot(String mode) {
|
void reboot(String mode) {
|
||||||
if (!Shell.rootAccess()) {
|
if (!Shell.rootAccess()) {
|
||||||
showAlert(getString(R.string.root_failed));
|
showAlert(getString(R.string.root_failed));
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,9 @@
|
||||||
package org.meowcat.edxposed.manager;
|
package org.meowcat.edxposed.manager;
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
@ -20,11 +16,9 @@ import android.widget.TextView;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.core.app.ActivityCompat;
|
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
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.NavUtil;
|
||||||
import org.meowcat.edxposed.manager.util.json.XposedTab;
|
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.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import static org.meowcat.edxposed.manager.XposedApp.WRITE_EXTERNAL_PERMISSION;
|
|
||||||
|
|
||||||
public class BaseAdvancedInstaller extends Fragment {
|
public class BaseAdvancedInstaller extends Fragment {
|
||||||
private View mClickedButton;
|
private View mClickedButton;
|
||||||
|
|
||||||
|
|
@ -88,16 +80,6 @@ public class BaseAdvancedInstaller extends Fragment {
|
||||||
return Objects.requireNonNull(tab).description;
|
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
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
|
@ -143,31 +125,21 @@ public class BaseAdvancedInstaller extends Fragment {
|
||||||
.setMessage(s).setPositiveButton(android.R.string.ok, null).show();
|
.setMessage(s).setPositiveButton(android.R.string.ok, null).show();
|
||||||
});
|
});
|
||||||
|
|
||||||
btnInstall.setOnClickListener(v -> {
|
btnInstall.setOnClickListener(v -> areYouSure(R.string.warningArchitecture,
|
||||||
mClickedButton = v;
|
|
||||||
if (checkPermissions()) return;
|
|
||||||
|
|
||||||
areYouSure(R.string.warningArchitecture,
|
|
||||||
(dialog, which) -> {
|
(dialog, which) -> {
|
||||||
XposedZip selectedInstaller = (XposedZip) chooserInstallers.getSelectedItem();
|
XposedZip selectedInstaller = (XposedZip) chooserInstallers.getSelectedItem();
|
||||||
Uri uri = Uri.parse(selectedInstaller.link);
|
Uri uri = Uri.parse(selectedInstaller.link);
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
});
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
btnUninstall.setOnClickListener(v -> {
|
btnUninstall.setOnClickListener(v -> areYouSure(R.string.warningArchitecture,
|
||||||
mClickedButton = v;
|
|
||||||
if (checkPermissions()) return;
|
|
||||||
|
|
||||||
areYouSure(R.string.warningArchitecture,
|
|
||||||
(dialog, which) -> {
|
(dialog, which) -> {
|
||||||
XposedZip selectedUninstaller = (XposedZip) chooserUninstallers.getSelectedItem();
|
XposedZip selectedUninstaller = (XposedZip) chooserUninstallers.getSelectedItem();
|
||||||
Uri uri = Uri.parse(selectedUninstaller.link);
|
Uri uri = Uri.parse(selectedUninstaller.link);
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
});
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
noticeTv.setText(Html.fromHtml(notice()));
|
noticeTv.setText(Html.fromHtml(notice()));
|
||||||
author.setText(getString(R.string.download_author, author()));
|
author.setText(getString(R.string.download_author, author()));
|
||||||
|
|
@ -198,20 +170,6 @@ public class BaseAdvancedInstaller extends Fragment {
|
||||||
return view;
|
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")
|
@SuppressWarnings("SameParameterValue")
|
||||||
private void areYouSure(int contentTextId, DialogInterface.OnClickListener listener) {
|
private void areYouSure(int contentTextId, DialogInterface.OnClickListener listener) {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.widget.SearchView;
|
import androidx.appcompat.widget.SearchView;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import androidx.core.view.MenuItemCompat;
|
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
@ -73,7 +72,7 @@ public class BlackListActivity extends BaseActivity implements AppAdapter.Callba
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
|
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
|
||||||
getMenuInflater().inflate(R.menu.menu_app_list, 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);
|
mSearchView.setOnQueryTextListener(mSearchListener);
|
||||||
return super.onCreateOptionsMenu(menu);
|
return super.onCreateOptionsMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
@ -126,11 +125,6 @@ public class BlackListActivity extends BaseActivity implements AppAdapter.Callba
|
||||||
AppHelper.showMenu(this, getSupportFragmentManager(), v, info);
|
AppHelper.showMenu(this, getSupportFragmentManager(), v, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPointerCaptureChanged(boolean hasCapture) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
if (mSearchView.isIconified()) {
|
if (mSearchView.isIconified()) {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.widget.SearchView;
|
import androidx.appcompat.widget.SearchView;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import androidx.core.view.MenuItemCompat;
|
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
@ -68,15 +67,10 @@ public class CompatListActivity extends BaseActivity implements AppAdapter.Callb
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
|
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
|
||||||
getMenuInflater().inflate(R.menu.menu_app_list, 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);
|
mSearchView.setOnQueryTextListener(mSearchListener);
|
||||||
return super.onCreateOptionsMenu(menu);
|
return super.onCreateOptionsMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.widget.SearchView;
|
import androidx.appcompat.widget.SearchView;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import androidx.core.view.MenuItemCompat;
|
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
@ -153,8 +152,7 @@ public class DownloadActivity extends BaseActivity implements RepoLoader.RepoLis
|
||||||
getMenuInflater().inflate(R.menu.menu_download, menu);
|
getMenuInflater().inflate(R.menu.menu_download, menu);
|
||||||
|
|
||||||
// Setup search button
|
// Setup search button
|
||||||
final MenuItem searchItem = menu.findItem(R.id.menu_search);
|
mSearchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
|
||||||
mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);
|
|
||||||
mSearchView.setIconifiedByDefault(true);
|
mSearchView.setIconifiedByDefault(true);
|
||||||
mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package org.meowcat.edxposed.manager;
|
package org.meowcat.edxposed.manager;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.text.method.LinkMovementMethod;
|
||||||
|
|
@ -20,6 +21,7 @@ import org.meowcat.edxposed.manager.util.chrome.LinkTransformationMethod;
|
||||||
|
|
||||||
public class DownloadDetailsFragment extends Fragment {
|
public class DownloadDetailsFragment extends Fragment {
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
DownloadDetailsActivity mActivity = (DownloadDetailsActivity) getActivity();
|
DownloadDetailsActivity mActivity = (DownloadDetailsActivity) getActivity();
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ public class DownloadDetailsSettingsFragment extends PreferenceFragmentCompat {
|
||||||
|
|
||||||
if (prefs.getBoolean("no_global", true)) {
|
if (prefs.getBoolean("no_global", true)) {
|
||||||
for (Map.Entry<String, ?> k : prefs.getAll().entrySet()) {
|
for (Map.Entry<String, ?> k : prefs.getAll().entrySet()) {
|
||||||
if (prefs.getString(k.getKey(), "").equals("global")) {
|
if (("global").equals(prefs.getString(k.getKey(), ""))) {
|
||||||
editor.putString(k.getKey(), "").apply();
|
editor.putString(k.getKey(), "").apply();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,16 @@
|
||||||
package org.meowcat.edxposed.manager;
|
package org.meowcat.edxposed.manager;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.text.method.LinkMovementMethod;
|
||||||
|
import android.util.Log;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
@ -35,11 +39,11 @@ import org.meowcat.edxposed.manager.util.chrome.LinkTransformationMethod;
|
||||||
import org.meowcat.edxposed.manager.widget.DownloadView;
|
import org.meowcat.edxposed.manager.widget.DownloadView;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import static org.meowcat.edxposed.manager.XposedApp.WRITE_EXTERNAL_PERMISSION;
|
|
||||||
|
|
||||||
public class DownloadDetailsVersionsFragment extends ListFragment {
|
public class DownloadDetailsVersionsFragment extends ListFragment {
|
||||||
private static View rootView;
|
private static View rootView;
|
||||||
private DownloadDetailsActivity mActivity;
|
private DownloadDetailsActivity mActivity;
|
||||||
|
|
@ -93,13 +97,31 @@ public class DownloadDetailsVersionsFragment extends ListFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
if (requestCode == WRITE_EXTERNAL_PERMISSION) {
|
if (resultCode != Activity.RESULT_OK) {
|
||||||
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
return;
|
||||||
DownloadView.mClickedButton.performClick();
|
}
|
||||||
} else {
|
if (requestCode == 42) {
|
||||||
Snackbar.make(rootView, R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show();
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,10 +83,6 @@ public class EdDownloadActivity extends BaseActivity {
|
||||||
@SuppressLint("StaticFieldLeak")
|
@SuppressLint("StaticFieldLeak")
|
||||||
private class JSONParser extends AsyncTask<Void, Void, String> {
|
private class JSONParser extends AsyncTask<Void, Void, String> {
|
||||||
|
|
||||||
private String newApkVersion = null;
|
|
||||||
private String newApkLink = null;
|
|
||||||
private String newApkChangelog = null;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String doInBackground(Void... params) {
|
protected String doInBackground(Void... params) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -115,9 +111,9 @@ public class EdDownloadActivity extends BaseActivity {
|
||||||
tabsAdapter.notifyDataSetChanged();
|
tabsAdapter.notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
newApkVersion = xposedJson.apk.version;
|
String newApkVersion = xposedJson.apk.version;
|
||||||
newApkLink = xposedJson.apk.link;
|
String newApkLink = xposedJson.apk.link;
|
||||||
newApkChangelog = xposedJson.apk.changelog;
|
String newApkChangelog = xposedJson.apk.changelog;
|
||||||
|
|
||||||
if (newApkVersion == null) {
|
if (newApkVersion == null) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,11 @@
|
||||||
package org.meowcat.edxposed.manager;
|
package org.meowcat.edxposed.manager;
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
|
|
@ -23,7 +19,6 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import androidx.core.app.ActivityCompat;
|
|
||||||
import androidx.core.content.FileProvider;
|
import androidx.core.content.FileProvider;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
@ -36,6 +31,7 @@ import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
|
@ -48,7 +44,6 @@ public class LogsActivity extends BaseActivity {
|
||||||
XposedApp.BASE_DIR + "log/error.log.old");
|
XposedApp.BASE_DIR + "log/error.log.old");
|
||||||
private File mFileAllLog = new File(XposedApp.BASE_DIR + "log/all.log");
|
private File mFileAllLog = new File(XposedApp.BASE_DIR + "log/all.log");
|
||||||
private File mFileAllLogOld = new File(XposedApp.BASE_DIR + "log/all.log.old");
|
private File mFileAllLogOld = new File(XposedApp.BASE_DIR + "log/all.log.old");
|
||||||
private MenuItem mClickedMenuItem = null;
|
|
||||||
private LogsAdapter mAdapter;
|
private LogsAdapter mAdapter;
|
||||||
private RecyclerView mListView;
|
private RecyclerView mListView;
|
||||||
private Handler handler = new Handler();
|
private Handler handler = new Handler();
|
||||||
|
|
@ -124,7 +119,6 @@ public class LogsActivity extends BaseActivity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
mClickedMenuItem = item;
|
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.menu_scroll_top:
|
case R.id.menu_scroll_top:
|
||||||
scrollTop();
|
scrollTop();
|
||||||
|
|
@ -138,7 +132,8 @@ public class LogsActivity extends BaseActivity {
|
||||||
case R.id.menu_send:
|
case R.id.menu_send:
|
||||||
try {
|
try {
|
||||||
send();
|
send();
|
||||||
} catch (NullPointerException ignored) {
|
} catch (Exception e) {
|
||||||
|
Snackbar.make(findViewById(R.id.snackbar), e.getLocalizedMessage(), Snackbar.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_save:
|
case R.id.menu_save:
|
||||||
|
|
@ -186,34 +181,8 @@ public class LogsActivity extends BaseActivity {
|
||||||
startActivity(Intent.createChooser(sendIntent, getResources().getString(R.string.menuSend)));
|
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")
|
@SuppressLint("DefaultLocale")
|
||||||
private void save() {
|
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();
|
Calendar now = Calendar.getInstance();
|
||||||
String filename = String.format(
|
String filename = String.format(
|
||||||
"EdXposed_Verbose_%04d%02d%02d_%02d%02d%02d.log",
|
"EdXposed_Verbose_%04d%02d%02d_%02d%02d%02d.log",
|
||||||
|
|
@ -221,24 +190,41 @@ public class LogsActivity extends BaseActivity {
|
||||||
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
|
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
|
||||||
now.get(Calendar.MINUTE), now.get(Calendar.SECOND));
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 {
|
try {
|
||||||
|
OutputStream os = getContentResolver().openOutputStream(uri);
|
||||||
|
if (os != null) {
|
||||||
FileInputStream in = new FileInputStream(allLog ? mFileAllLog : mFileErrorLog);
|
FileInputStream in = new FileInputStream(allLog ? mFileAllLog : mFileErrorLog);
|
||||||
FileOutputStream out = new FileOutputStream(targetFile);
|
|
||||||
byte[] buffer = new byte[1024];
|
byte[] buffer = new byte[1024];
|
||||||
int len;
|
int len;
|
||||||
while ((len = in.read(buffer)) > 0) {
|
while ((len = in.read(buffer)) > 0) {
|
||||||
out.write(buffer, 0, len);
|
os.write(buffer, 0, len);
|
||||||
}
|
}
|
||||||
in.close();
|
os.close();
|
||||||
out.close();
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
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();
|
Snackbar.make(findViewById(R.id.snackbar), getResources().getString(R.string.logs_save_failed) + "\n" + e.getMessage(), Snackbar.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
@SuppressLint("StaticFieldLeak")
|
||||||
private class LogsReader extends AsyncTask<File, Integer, ArrayList<String>> {
|
private class LogsReader extends AsyncTask<File, Integer, ArrayList<String>> {
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,6 @@ import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.view.menu.MenuBuilder;
|
|
||||||
import androidx.appcompat.view.menu.MenuPopupHelper;
|
|
||||||
import androidx.appcompat.widget.PopupMenu;
|
import androidx.appcompat.widget.PopupMenu;
|
||||||
|
|
||||||
import com.google.android.material.card.MaterialCardView;
|
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);
|
PopupMenu appMenu = new PopupMenu(MainActivity.this, menu);
|
||||||
appMenu.inflate(R.menu.menu_installer);
|
appMenu.inflate(R.menu.menu_installer);
|
||||||
appMenu.setOnMenuItemClickListener(this::onOptionsItemSelected);
|
appMenu.setOnMenuItemClickListener(this::onOptionsItemSelected);
|
||||||
MenuPopupHelper menuHelper = new MenuPopupHelper(MainActivity.this, (MenuBuilder) appMenu.getMenu(), menu);
|
appMenu.show();
|
||||||
menuHelper.setForceShowIcon(true);
|
|
||||||
menuHelper.show();
|
|
||||||
});
|
});
|
||||||
String installedXposedVersion;
|
String installedXposedVersion;
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,11 @@
|
||||||
package org.meowcat.edxposed.manager;
|
package org.meowcat.edxposed.manager;
|
||||||
|
|
||||||
import android.content.ActivityNotFoundException;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
@ -24,7 +20,6 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.widget.SearchView;
|
import androidx.appcompat.widget.SearchView;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import androidx.core.view.MenuItemCompat;
|
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
@ -43,15 +38,11 @@ import org.meowcat.edxposed.manager.util.NavUtil;
|
||||||
import org.meowcat.edxposed.manager.util.RepoLoader;
|
import org.meowcat.edxposed.manager.util.RepoLoader;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.BufferedWriter;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
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.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.SimpleDateFormat;
|
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 DateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
|
||||||
private ModuleUtil mModuleUtil;
|
private ModuleUtil mModuleUtil;
|
||||||
private ModuleAdapter mAdapter = null;
|
private ModuleAdapter mAdapter = null;
|
||||||
private MenuItem mClickedMenuItem = null;
|
|
||||||
private RecyclerView mListView;
|
private RecyclerView mListView;
|
||||||
private SwipeRefreshLayout mSwipeRefreshLayout;
|
private SwipeRefreshLayout mSwipeRefreshLayout;
|
||||||
private Runnable reloadModules = new Runnable() {
|
private Runnable reloadModules = new Runnable() {
|
||||||
|
|
@ -162,152 +152,140 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
getMenuInflater().inflate(R.menu.menu_modules, 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);
|
mSearchView.setOnQueryTextListener(mSearchListener);
|
||||||
return super.onCreateOptionsMenu(menu);
|
return super.onCreateOptionsMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions,
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
grantResults);
|
if (resultCode != RESULT_OK) {
|
||||||
if (requestCode == XposedApp.WRITE_EXTERNAL_PERMISSION) {
|
return;
|
||||||
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
}
|
||||||
if (mClickedMenuItem != null) {
|
if (requestCode == 42) {
|
||||||
new Handler().postDelayed(() -> onOptionsItemSelected(mClickedMenuItem), 500);
|
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<String> 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
|
@Override
|
||||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
File enabledModulesPath = new File(XposedApp.createFolder(), "enabled_modules.list");
|
Intent intent;
|
||||||
File installedModulesPath = new File(XposedApp.createFolder(), "installed_modules.list");
|
|
||||||
File listModules = new File(XposedApp.ENABLED_MODULES_LIST_FILE);
|
|
||||||
|
|
||||||
mClickedMenuItem = item;
|
|
||||||
|
|
||||||
if (checkPermissions())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.export_enabled_modules:
|
case R.id.export_enabled_modules:
|
||||||
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ModuleUtil.getInstance().getEnabledModules().isEmpty()) {
|
if (ModuleUtil.getInstance().getEnabledModules().isEmpty()) {
|
||||||
Snackbar.make(findViewById(R.id.snackbar), R.string.no_enabled_modules, Snackbar.LENGTH_SHORT).show();
|
Snackbar.make(findViewById(R.id.snackbar), R.string.no_enabled_modules, Snackbar.LENGTH_SHORT).show();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
||||||
try {
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
XposedApp.createFolder();
|
intent.setType("text/*");
|
||||||
|
intent.putExtra(Intent.EXTRA_TITLE, "enabled_modules.list");
|
||||||
FileInputStream in = new FileInputStream(listModules);
|
startActivityForResult(intent, 42);
|
||||||
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();
|
|
||||||
return true;
|
return true;
|
||||||
case R.id.export_installed_modules:
|
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<String, ModuleUtil.InstalledModule> installedModules = ModuleUtil.getInstance().getModules();
|
Map<String, ModuleUtil.InstalledModule> installedModules = ModuleUtil.getInstance().getModules();
|
||||||
|
|
||||||
if (installedModules.isEmpty()) {
|
if (installedModules.isEmpty()) {
|
||||||
Snackbar.make(findViewById(R.id.snackbar), R.string.no_installed_modules, Snackbar.LENGTH_SHORT).show();
|
Snackbar.make(findViewById(R.id.snackbar), R.string.no_installed_modules, Snackbar.LENGTH_SHORT).show();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
||||||
try {
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
XposedApp.createFolder();
|
intent.setType("text/*");
|
||||||
|
intent.putExtra(Intent.EXTRA_TITLE, "installed_modules.list");
|
||||||
FileWriter fw = new FileWriter(installedModulesPath);
|
startActivityForResult(intent, 43);
|
||||||
BufferedWriter bw = new BufferedWriter(fw);
|
|
||||||
PrintWriter fileOut = new PrintWriter(bw);
|
|
||||||
|
|
||||||
Set<String> 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();
|
|
||||||
return true;
|
return true;
|
||||||
case R.id.import_installed_modules:
|
case R.id.import_installed_modules:
|
||||||
return importModules(installedModulesPath);
|
|
||||||
case R.id.import_enabled_modules:
|
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);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean importModules(File path) {
|
private void importModules(Uri uri) {
|
||||||
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;
|
|
||||||
RepoLoader repoLoader = RepoLoader.getInstance();
|
RepoLoader repoLoader = RepoLoader.getInstance();
|
||||||
List<Module> list = new ArrayList<>();
|
List<Module> 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 {
|
try {
|
||||||
assert ips != null;
|
InputStream inputStream = getContentResolver().openInputStream(uri);
|
||||||
InputStreamReader ipsr = new InputStreamReader(ips);
|
InputStreamReader isr = new InputStreamReader(inputStream);
|
||||||
BufferedReader br = new BufferedReader(ipsr);
|
BufferedReader br = new BufferedReader(isr);
|
||||||
String line;
|
String line;
|
||||||
while ((line = br.readLine()) != null) {
|
while ((line = br.readLine()) != null) {
|
||||||
Module m = repoLoader.getModule(line);
|
Module m = repoLoader.getModule(line);
|
||||||
|
|
||||||
if (m == null) {
|
if (m == null) {
|
||||||
Snackbar.make(findViewById(R.id.snackbar), getString(R.string.download_details_not_found,
|
Snackbar.make(findViewById(R.id.snackbar), getString(R.string.download_details_not_found, line), Snackbar.LENGTH_SHORT).show();
|
||||||
line), Snackbar.LENGTH_SHORT).show();
|
|
||||||
} else {
|
} else {
|
||||||
list.add(m);
|
list.add(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
br.close();
|
br.close();
|
||||||
} catch (ActivityNotFoundException | IOException e) {
|
} catch (Exception e) {
|
||||||
Snackbar.make(findViewById(R.id.snackbar), e.toString(), Snackbar.LENGTH_SHORT).show();
|
e.printStackTrace();
|
||||||
|
Snackbar.make(findViewById(R.id.snackbar), e.getLocalizedMessage(), Snackbar.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final Module m : list) {
|
for (final Module m : list) {
|
||||||
|
if (mModuleUtil.getModule(m.packageName) != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
ModuleVersion mv = null;
|
ModuleVersion mv = null;
|
||||||
for (int i = 0; i < m.versions.size(); i++) {
|
for (int i = 0; i < m.versions.size(); i++) {
|
||||||
ModuleVersion mvTemp = m.versions.get(i);
|
ModuleVersion mvTemp = m.versions.get(i);
|
||||||
|
|
@ -319,13 +297,11 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mv != null) {
|
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();
|
ModuleUtil.getInstance().reloadInstalledModules();
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,9 @@ package org.meowcat.edxposed.manager;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
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.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.FileUtils;
|
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
|
@ -28,13 +24,10 @@ import java.io.File;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
@SuppressLint("StaticFieldLeak")
|
||||||
public class StatusInstallerFragment extends Fragment {
|
public class StatusInstallerFragment extends Fragment {
|
||||||
|
|
||||||
public static final File DISABLE_FILE = new File(XposedApp.BASE_DIR + "conf/disabled");
|
|
||||||
private static AppCompatActivity sActivity;
|
private static AppCompatActivity sActivity;
|
||||||
private static String mUpdateLink;
|
private static String mUpdateLink;
|
||||||
private static View mUpdateView;
|
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
|
@Override
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
@ -119,7 +98,6 @@ public class StatusInstallerFragment extends Fragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("WorldReadableFiles")
|
@SuppressLint("WorldReadableFiles")
|
||||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
View v = inflater.inflate(R.layout.status_installer, container, false);
|
View v = inflater.inflate(R.layout.status_installer, container, false);
|
||||||
|
|
@ -156,8 +134,6 @@ public class StatusInstallerFragment extends Fragment {
|
||||||
cpu.setText(getCompleteArch());
|
cpu.setText(getCompleteArch());
|
||||||
|
|
||||||
determineVerifiedBootState(v);
|
determineVerifiedBootState(v);
|
||||||
|
|
||||||
refreshKnownIssue();
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -191,6 +167,7 @@ public class StatusInstallerFragment extends Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
@SuppressWarnings("SameParameterValue")
|
@SuppressWarnings("SameParameterValue")
|
||||||
private boolean checkAppInstalled(Context context, String pkgName) {
|
private boolean checkAppInstalled(Context context, String pkgName) {
|
||||||
if (pkgName == null || pkgName.isEmpty()) {
|
if (pkgName == null || pkgName.isEmpty()) {
|
||||||
|
|
@ -241,7 +218,7 @@ public class StatusInstallerFragment extends Fragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
private String getAndroidVersion() {
|
private String getAndroidVersion() {
|
||||||
switch (Build.VERSION.SDK_INT) {
|
switch (Build.VERSION.SDK_INT) {
|
||||||
// case 16:
|
// case 16:
|
||||||
|
|
@ -296,6 +273,7 @@ public class StatusInstallerFragment extends Fragment {
|
||||||
return manufacturer;
|
return manufacturer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
private File getCanonicalFile(File file) {
|
private File getCanonicalFile(File file) {
|
||||||
try {
|
try {
|
||||||
return file.getCanonicalFile();
|
return file.getCanonicalFile();
|
||||||
|
|
@ -312,7 +290,7 @@ public class StatusInstallerFragment extends Fragment {
|
||||||
return file.getAbsolutePath() + " \u2192 " + canonical.getAbsolutePath();
|
return file.getAbsolutePath() + " \u2192 " + canonical.getAbsolutePath();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
private int extractIntPart(String str) {
|
private int extractIntPart(String str) {
|
||||||
int result = 0, length = str.length();
|
int result = 0, length = str.length();
|
||||||
for (int offset = 0; offset < length; offset++) {
|
for (int offset = 0; offset < length; offset++) {
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,7 @@ public class XposedApp extends de.robv.android.xposed.installer.XposedApp implem
|
||||||
|
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
if (!BuildConfig.DEBUG) {
|
||||||
try {
|
try {
|
||||||
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
|
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
|
||||||
|
|
||||||
|
|
@ -129,7 +130,10 @@ public class XposedApp extends de.robv.android.xposed.installer.XposedApp implem
|
||||||
System.exit(10);
|
System.exit(10);
|
||||||
});
|
});
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mInstance = this;
|
mInstance = this;
|
||||||
mUiThread = Thread.currentThread();
|
mUiThread = Thread.currentThread();
|
||||||
mMainHandler = new Handler();
|
mMainHandler = new Handler();
|
||||||
|
|
|
||||||
|
|
@ -4,26 +4,17 @@ import android.annotation.SuppressLint;
|
||||||
import android.app.DownloadManager;
|
import android.app.DownloadManager;
|
||||||
import android.app.DownloadManager.Query;
|
import android.app.DownloadManager.Query;
|
||||||
import android.app.DownloadManager.Request;
|
import android.app.DownloadManager.Request;
|
||||||
import android.app.ProgressDialog;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Environment;
|
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
import android.util.Log;
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
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.R;
|
||||||
import org.meowcat.edxposed.manager.XposedApp;
|
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.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
|
@ -40,7 +31,7 @@ import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class DownloadsUtil {
|
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 String MIME_TYPE_ZIP = "application/zip";
|
||||||
private static final Map<String, DownloadFinishedCallback> mCallbacks = new HashMap<>();
|
private static final Map<String, DownloadFinishedCallback> mCallbacks = new HashMap<>();
|
||||||
@SuppressLint("StaticFieldLeak")
|
@SuppressLint("StaticFieldLeak")
|
||||||
|
|
@ -48,141 +39,44 @@ public class DownloadsUtil {
|
||||||
private static final SharedPreferences mPref = mApp
|
private static final SharedPreferences mPref = mApp
|
||||||
.getSharedPreferences("download_cache", Context.MODE_PRIVATE);
|
.getSharedPreferences("download_cache", Context.MODE_PRIVATE);
|
||||||
|
|
||||||
private static String DOWNLOAD_MODULES = "modules";
|
|
||||||
|
|
||||||
private static DownloadInfo add(Builder b) {
|
private static DownloadInfo add(Builder b) {
|
||||||
Context context = b.mContext;
|
Context context = b.mContext;
|
||||||
removeAllForUrl(context, b.mUrl);
|
removeAllForUrl(context, b.mUrl);
|
||||||
|
|
||||||
if (!b.mDialog) {
|
|
||||||
synchronized (mCallbacks) {
|
synchronized (mCallbacks) {
|
||||||
mCallbacks.put(b.mUrl, b.mCallback);
|
mCallbacks.put(b.mUrl, b.mCallback);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
String savePath = "Download/EdXposedManager";
|
|
||||||
if (b.mModule) {
|
|
||||||
savePath += "/modules";
|
|
||||||
}
|
|
||||||
|
|
||||||
Request request = new Request(Uri.parse(b.mUrl));
|
Request request = new Request(Uri.parse(b.mUrl));
|
||||||
request.setTitle(b.mTitle);
|
request.setTitle(b.mTitle);
|
||||||
request.setMimeType(b.mMimeType.toString());
|
request.setMimeType(b.mMimeType.toString());
|
||||||
if (b.mSave) {
|
/*if (b.mSave) {
|
||||||
try {
|
try {
|
||||||
request.setDestinationInExternalPublicDir(savePath, b.mTitle + b.mMimeType.getExtension());
|
request.setDestinationInExternalPublicDir(savePath, b.mTitle + b.mMimeType.getExtension());
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
} else if (b.mDestination != null) {
|
} else */
|
||||||
//noinspection ResultOfMethodCallIgnored
|
File destination = new File(context.getExternalCacheDir(), "/downloads/" + b.mTitle + b.mMimeType.getExtension());
|
||||||
b.mDestination.getParentFile().mkdirs();
|
request.setDestinationUri(Uri.fromFile(destination));
|
||||||
removeAllForLocalFile(context, b.mDestination);
|
|
||||||
request.setDestinationUri(Uri.fromFile(b.mDestination));
|
|
||||||
}
|
|
||||||
|
|
||||||
request.setNotificationVisibility(Request.VISIBILITY_VISIBLE);
|
request.setNotificationVisibility(Request.VISIBILITY_VISIBLE);
|
||||||
|
|
||||||
DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
|
DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
|
||||||
long id = dm.enqueue(request);
|
long id = dm.enqueue(request);
|
||||||
|
|
||||||
if (b.mDialog) {
|
|
||||||
showDownloadDialog(b, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return getById(context, id);
|
return getById(context, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static File[] getDownloadDirs(String subDir) {
|
public static DownloadInfo addModule(Context context, String title, String url, DownloadFinishedCallback callback) {
|
||||||
Context context = XposedApp.getInstance();
|
|
||||||
ArrayList<File> 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) {
|
|
||||||
return new Builder(context)
|
return new Builder(context)
|
||||||
.setTitle(title)
|
.setTitle(title)
|
||||||
.setUrl(url)
|
.setUrl(url)
|
||||||
.setDestinationFromUrl(DownloadsUtil.DOWNLOAD_MODULES)
|
|
||||||
.setCallback(callback)
|
.setCallback(callback)
|
||||||
.setSave(save)
|
|
||||||
.setModule(true)
|
.setModule(true)
|
||||||
.setMimeType(MIME_TYPES.APK)
|
.setMimeType(MIME_TYPES.APK)
|
||||||
.download();
|
.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));
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ModuleVersion getStableVersion(Module m) {
|
public static ModuleVersion getStableVersion(Module m) {
|
||||||
for (int i = 0; i < m.versions.size(); i++) {
|
for (int i = 0; i < m.versions.size(); i++) {
|
||||||
ModuleVersion mvTemp = m.versions.get(i);
|
ModuleVersion mvTemp = m.versions.get(i);
|
||||||
|
|
@ -193,7 +87,7 @@ public class DownloadsUtil {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
public static DownloadInfo getById(Context context, long id) {
|
public static DownloadInfo getById(Context context, long id) {
|
||||||
DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
|
DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
|
||||||
Cursor c = dm.query(new Query().setFilterById(id));
|
Cursor c = dm.query(new Query().setFilterById(id));
|
||||||
|
|
@ -317,7 +211,7 @@ public class DownloadsUtil {
|
||||||
|
|
||||||
dm.remove(ids);
|
dm.remove(ids);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
private static void removeAllForLocalFile(Context context, File file) {
|
private static void removeAllForLocalFile(Context context, File file) {
|
||||||
//noinspection ResultOfMethodCallIgnored
|
//noinspection ResultOfMethodCallIgnored
|
||||||
file.delete();
|
file.delete();
|
||||||
|
|
@ -367,7 +261,7 @@ public class DownloadsUtil {
|
||||||
ids[i] = idsList.get(i);
|
ids[i] = idsList.get(i);
|
||||||
|
|
||||||
dm.remove(ids);
|
dm.remove(ids);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
// public static void removeOutdated(Context context, long cutoff) {
|
// public static void removeOutdated(Context context, long cutoff) {
|
||||||
// DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
|
// DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
|
||||||
|
|
@ -563,9 +457,6 @@ public class DownloadsUtil {
|
||||||
private String mUrl = null;
|
private String mUrl = null;
|
||||||
private DownloadFinishedCallback mCallback = null;
|
private DownloadFinishedCallback mCallback = null;
|
||||||
private MIME_TYPES mMimeType = MIME_TYPES.APK;
|
private MIME_TYPES mMimeType = MIME_TYPES.APK;
|
||||||
private File mDestination = null;
|
|
||||||
private boolean mDialog = false;
|
|
||||||
private boolean mSave = false;
|
|
||||||
|
|
||||||
public Builder(Context context) {
|
public Builder(Context context) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
|
|
@ -591,33 +482,11 @@ public class DownloadsUtil {
|
||||||
return this;
|
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) {
|
public Builder setModule(boolean module) {
|
||||||
this.mModule = module;
|
this.mModule = module;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setDialog(boolean dialog) {
|
|
||||||
mDialog = dialog;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DownloadInfo download() {
|
public DownloadInfo download() {
|
||||||
return add(this);
|
return add(this);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
package org.meowcat.edxposed.manager.widget;
|
package org.meowcat.edxposed.manager.widget;
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.DownloadManager;
|
import android.app.DownloadManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.Intent;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
@ -12,9 +10,7 @@ import android.widget.Button;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.core.app.ActivityCompat;
|
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import org.meowcat.edxposed.manager.R;
|
import org.meowcat.edxposed.manager.R;
|
||||||
|
|
@ -23,20 +19,15 @@ import org.meowcat.edxposed.manager.util.DownloadsUtil.DownloadFinishedCallback;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import static org.meowcat.edxposed.manager.XposedApp.WRITE_EXTERNAL_PERMISSION;
|
|
||||||
|
|
||||||
public class DownloadView extends LinearLayout {
|
public class DownloadView extends LinearLayout {
|
||||||
@SuppressLint("StaticFieldLeak")
|
public static DownloadsUtil.DownloadInfo mInfo = null;
|
||||||
public static Button mClickedButton;
|
|
||||||
private final Button btnDownload;
|
private final Button btnDownload;
|
||||||
private final Button btnDownloadCancel;
|
private final Button btnDownloadCancel;
|
||||||
private final Button btnInstall;
|
private final Button btnInstall;
|
||||||
private final Button btnRemove;
|
|
||||||
private final Button btnSave;
|
private final Button btnSave;
|
||||||
private final ProgressBar progressBar;
|
private final ProgressBar progressBar;
|
||||||
private final TextView txtInfo;
|
private final TextView txtInfo;
|
||||||
public Fragment fragment;
|
public Fragment fragment;
|
||||||
private DownloadsUtil.DownloadInfo mInfo = null;
|
|
||||||
private String mUrl = null;
|
private String mUrl = null;
|
||||||
private final Runnable refreshViewRunnable = new Runnable() {
|
private final Runnable refreshViewRunnable = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -45,7 +36,6 @@ public class DownloadView extends LinearLayout {
|
||||||
btnDownload.setVisibility(View.GONE);
|
btnDownload.setVisibility(View.GONE);
|
||||||
btnSave.setVisibility(View.GONE);
|
btnSave.setVisibility(View.GONE);
|
||||||
btnDownloadCancel.setVisibility(View.GONE);
|
btnDownloadCancel.setVisibility(View.GONE);
|
||||||
btnRemove.setVisibility(View.GONE);
|
|
||||||
btnInstall.setVisibility(View.GONE);
|
btnInstall.setVisibility(View.GONE);
|
||||||
progressBar.setVisibility(View.GONE);
|
progressBar.setVisibility(View.GONE);
|
||||||
txtInfo.setVisibility(View.VISIBLE);
|
txtInfo.setVisibility(View.VISIBLE);
|
||||||
|
|
@ -54,7 +44,6 @@ public class DownloadView extends LinearLayout {
|
||||||
btnDownload.setVisibility(View.VISIBLE);
|
btnDownload.setVisibility(View.VISIBLE);
|
||||||
btnSave.setVisibility(View.VISIBLE);
|
btnSave.setVisibility(View.VISIBLE);
|
||||||
btnDownloadCancel.setVisibility(View.GONE);
|
btnDownloadCancel.setVisibility(View.GONE);
|
||||||
btnRemove.setVisibility(View.GONE);
|
|
||||||
btnInstall.setVisibility(View.GONE);
|
btnInstall.setVisibility(View.GONE);
|
||||||
progressBar.setVisibility(View.GONE);
|
progressBar.setVisibility(View.GONE);
|
||||||
txtInfo.setVisibility(View.GONE);
|
txtInfo.setVisibility(View.GONE);
|
||||||
|
|
@ -66,7 +55,6 @@ public class DownloadView extends LinearLayout {
|
||||||
btnDownload.setVisibility(View.GONE);
|
btnDownload.setVisibility(View.GONE);
|
||||||
btnSave.setVisibility(View.GONE);
|
btnSave.setVisibility(View.GONE);
|
||||||
btnDownloadCancel.setVisibility(View.VISIBLE);
|
btnDownloadCancel.setVisibility(View.VISIBLE);
|
||||||
btnRemove.setVisibility(View.GONE);
|
|
||||||
btnInstall.setVisibility(View.GONE);
|
btnInstall.setVisibility(View.GONE);
|
||||||
progressBar.setVisibility(View.VISIBLE);
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
txtInfo.setVisibility(View.VISIBLE);
|
txtInfo.setVisibility(View.VISIBLE);
|
||||||
|
|
@ -88,7 +76,6 @@ public class DownloadView extends LinearLayout {
|
||||||
btnDownload.setVisibility(View.VISIBLE);
|
btnDownload.setVisibility(View.VISIBLE);
|
||||||
btnSave.setVisibility(View.VISIBLE);
|
btnSave.setVisibility(View.VISIBLE);
|
||||||
btnDownloadCancel.setVisibility(View.GONE);
|
btnDownloadCancel.setVisibility(View.GONE);
|
||||||
btnRemove.setVisibility(View.GONE);
|
|
||||||
btnInstall.setVisibility(View.GONE);
|
btnInstall.setVisibility(View.GONE);
|
||||||
progressBar.setVisibility(View.GONE);
|
progressBar.setVisibility(View.GONE);
|
||||||
txtInfo.setVisibility(View.VISIBLE);
|
txtInfo.setVisibility(View.VISIBLE);
|
||||||
|
|
@ -98,9 +85,8 @@ public class DownloadView extends LinearLayout {
|
||||||
|
|
||||||
case DownloadManager.STATUS_SUCCESSFUL:
|
case DownloadManager.STATUS_SUCCESSFUL:
|
||||||
btnDownload.setVisibility(View.GONE);
|
btnDownload.setVisibility(View.GONE);
|
||||||
btnSave.setVisibility(View.GONE);
|
btnSave.setVisibility(View.VISIBLE);
|
||||||
btnDownloadCancel.setVisibility(View.GONE);
|
btnDownloadCancel.setVisibility(View.GONE);
|
||||||
btnRemove.setVisibility(View.VISIBLE);
|
|
||||||
btnInstall.setVisibility(View.VISIBLE);
|
btnInstall.setVisibility(View.VISIBLE);
|
||||||
progressBar.setVisibility(View.GONE);
|
progressBar.setVisibility(View.GONE);
|
||||||
txtInfo.setVisibility(View.VISIBLE);
|
txtInfo.setVisibility(View.VISIBLE);
|
||||||
|
|
@ -123,27 +109,27 @@ public class DownloadView extends LinearLayout {
|
||||||
|
|
||||||
btnDownload = findViewById(R.id.btnDownload);
|
btnDownload = findViewById(R.id.btnDownload);
|
||||||
btnDownloadCancel = findViewById(R.id.btnDownloadCancel);
|
btnDownloadCancel = findViewById(R.id.btnDownloadCancel);
|
||||||
btnRemove = findViewById(R.id.btnRemove);
|
|
||||||
btnInstall = findViewById(R.id.btnInstall);
|
btnInstall = findViewById(R.id.btnInstall);
|
||||||
btnSave = findViewById(R.id.save);
|
btnSave = findViewById(R.id.save);
|
||||||
|
|
||||||
btnDownload.setOnClickListener(v -> {
|
btnDownload.setOnClickListener(v -> {
|
||||||
mClickedButton = btnDownload;
|
mInfo = DownloadsUtil.addModule(getContext(), mTitle, mUrl, mCallback);
|
||||||
|
|
||||||
mInfo = DownloadsUtil.addModule(getContext(), mTitle, mUrl, false, mCallback);
|
|
||||||
refreshViewFromUiThread();
|
refreshViewFromUiThread();
|
||||||
|
|
||||||
if (mInfo != null)
|
if (mInfo != null)
|
||||||
new DownloadMonitor().start();
|
new DownloadMonitor().start();
|
||||||
});
|
});
|
||||||
|
|
||||||
btnSave.setOnClickListener(v -> {
|
btnSave.setOnClickListener(v -> {
|
||||||
mClickedButton = btnSave;
|
mInfo = DownloadsUtil.addModule(getContext(), mTitle, mUrl, (context1, info) -> {
|
||||||
|
Intent exportIntent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
||||||
if (checkPermissions())
|
exportIntent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
return;
|
exportIntent.setType(DownloadsUtil.MIME_TYPE_APK);
|
||||||
|
exportIntent.putExtra(Intent.EXTRA_TITLE, mTitle + ".apk");
|
||||||
mInfo = DownloadsUtil.addModule(getContext(), mTitle, mUrl, true, (context1, info) -> Toast.makeText(context1, context1.getString(R.string.module_saved, info.localFilename), Toast.LENGTH_SHORT).show());
|
fragment.startActivityForResult(exportIntent, 42);
|
||||||
|
});
|
||||||
|
refreshViewFromUiThread();
|
||||||
|
if (mInfo != null)
|
||||||
|
new DownloadMonitor().start();
|
||||||
});
|
});
|
||||||
|
|
||||||
btnDownloadCancel.setOnClickListener(v -> {
|
btnDownloadCancel.setOnClickListener(v -> {
|
||||||
|
|
@ -154,14 +140,6 @@ public class DownloadView extends LinearLayout {
|
||||||
// UI update will happen automatically by the DownloadMonitor
|
// 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 -> {
|
btnInstall.setOnClickListener(v -> {
|
||||||
if (mCallback == null)
|
if (mCallback == null)
|
||||||
return;
|
return;
|
||||||
|
|
@ -175,15 +153,6 @@ public class DownloadView extends LinearLayout {
|
||||||
refreshViewFromUiThread();
|
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() {
|
private void refreshViewFromUiThread() {
|
||||||
refreshViewRunnable.run();
|
refreshViewRunnable.run();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,16 +42,6 @@
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:ignore="ButtonOrder" />
|
tools:ignore="ButtonOrder" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnRemove"
|
|
||||||
style="@style/Widget.MaterialComponents.Button"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="4dp"
|
|
||||||
android:text="@string/download_view_remove"
|
|
||||||
android:theme="@style/Widget.AppCompat.Button.Colored"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/btnInstall"
|
android:id="@+id/btnInstall"
|
||||||
style="@style/Widget.MaterialComponents.Button"
|
style="@style/Widget.MaterialComponents.Button"
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,6 @@
|
||||||
|
|
||||||
<!-- Logs tab -->
|
<!-- Logs tab -->
|
||||||
<string name="logs_save_failed">无法将日志写入 SD 卡: </string>
|
<string name="logs_save_failed">无法将日志写入 SD 卡: </string>
|
||||||
<string name="sdcard_not_writable">无法找到 SD 卡或不可写入</string>
|
|
||||||
<string name="menuClearLog">立即清理日志</string>
|
<string name="menuClearLog">立即清理日志</string>
|
||||||
<string name="logs_cleared">日志清理成功</string>
|
<string name="logs_cleared">日志清理成功</string>
|
||||||
<string name="logs_clear_failed">无法清理日志: </string>
|
<string name="logs_clear_failed">无法清理日志: </string>
|
||||||
|
|
@ -156,7 +155,6 @@
|
||||||
<string name="download_could_not_read_file">无法读取下载的文件: %s</string>
|
<string name="download_could_not_read_file">无法读取下载的文件: %s</string>
|
||||||
<string name="download_no_valid_apk">下载的文件并非有效的 APK (或存在兼容性问题)</string>
|
<string name="download_no_valid_apk">下载的文件并非有效的 APK (或存在兼容性问题)</string>
|
||||||
<string name="download_incorrect_package_name">文件包名不正确(下载: %1$s, 预期: %2$s)</string>
|
<string name="download_incorrect_package_name">文件包名不正确(下载: %1$s, 预期: %2$s)</string>
|
||||||
<string name="download_running">正在下载</string>
|
|
||||||
|
|
||||||
<!-- RepoLoader -->
|
<!-- RepoLoader -->
|
||||||
<string name="repo_download_failed_http">下载 %1$s 失败: %2$d(%3$s)</string>
|
<string name="repo_download_failed_http">下载 %1$s 失败: %2$d(%3$s)</string>
|
||||||
|
|
@ -173,7 +171,6 @@
|
||||||
<string name="no_installed_modules">没有任何已安装的模块</string>
|
<string name="no_installed_modules">没有任何已安装的模块</string>
|
||||||
<string name="enable_heads_up">启用浮动通知</string>
|
<string name="enable_heads_up">启用浮动通知</string>
|
||||||
<string name="enable_heads_up_summary">对新安装或已更新的模块启用浮动通知</string>
|
<string name="enable_heads_up_summary">对新安装或已更新的模块启用浮动通知</string>
|
||||||
<string name="no_backup_found">找不到备份</string>
|
|
||||||
<string name="no_enabled_modules">没有任何已启用的模块</string>
|
<string name="no_enabled_modules">没有任何已启用的模块</string>
|
||||||
|
|
||||||
<string name="share">分享</string>
|
<string name="share">分享</string>
|
||||||
|
|
@ -195,7 +192,6 @@
|
||||||
|
|
||||||
<string name="changelog">更新日志</string>
|
<string name="changelog">更新日志</string>
|
||||||
<string name="scroll_top">滚动到顶部</string>
|
<string name="scroll_top">滚动到顶部</string>
|
||||||
<string name="file_is_empty">文件为空</string>
|
|
||||||
<string name="install_with_su">静默安装(root)</string>
|
<string name="install_with_su">静默安装(root)</string>
|
||||||
<string name="install_with_su_summ">勾选后在 EdXposed Manager 内安装模块时将不再显示安装提示</string>
|
<string name="install_with_su_summ">勾选后在 EdXposed Manager 内安装模块时将不再显示安装提示</string>
|
||||||
<string name="module_saved">模块已保存至 %1$s</string>
|
<string name="module_saved">模块已保存至 %1$s</string>
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,6 @@
|
||||||
|
|
||||||
<!-- Logs tab -->
|
<!-- Logs tab -->
|
||||||
<string name="logs_save_failed">無法將日誌寫入 SD 卡: </string>
|
<string name="logs_save_failed">無法將日誌寫入 SD 卡: </string>
|
||||||
<string name="sdcard_not_writable">無法找到 SD 卡或不可寫入</string>
|
|
||||||
<string name="menuClearLog">立即清理日誌</string>
|
<string name="menuClearLog">立即清理日誌</string>
|
||||||
<string name="logs_cleared">日誌清理成功</string>
|
<string name="logs_cleared">日誌清理成功</string>
|
||||||
<string name="logs_clear_failed">無法清理日誌: </string>
|
<string name="logs_clear_failed">無法清理日誌: </string>
|
||||||
|
|
@ -156,7 +155,6 @@
|
||||||
<string name="download_could_not_read_file">無法讀取下載的文件: %s</string>
|
<string name="download_could_not_read_file">無法讀取下載的文件: %s</string>
|
||||||
<string name="download_no_valid_apk">下載的文件並非有效的 APK (或存在兼容性問題)</string>
|
<string name="download_no_valid_apk">下載的文件並非有效的 APK (或存在兼容性問題)</string>
|
||||||
<string name="download_incorrect_package_name">文件包名不正確(下載: %1$s, 預期: %2$s)</string>
|
<string name="download_incorrect_package_name">文件包名不正確(下載: %1$s, 預期: %2$s)</string>
|
||||||
<string name="download_running">正在下載</string>
|
|
||||||
|
|
||||||
<!-- RepoLoader -->
|
<!-- RepoLoader -->
|
||||||
<string name="repo_download_failed_http">下載 %1$s 失敗: %2$d(%3$s)</string>
|
<string name="repo_download_failed_http">下載 %1$s 失敗: %2$d(%3$s)</string>
|
||||||
|
|
@ -173,7 +171,6 @@
|
||||||
<string name="no_installed_modules">沒有任何已安裝的模塊</string>
|
<string name="no_installed_modules">沒有任何已安裝的模塊</string>
|
||||||
<string name="enable_heads_up">啟用浮動通知</string>
|
<string name="enable_heads_up">啟用浮動通知</string>
|
||||||
<string name="enable_heads_up_summary">對新安裝或已更新的模塊啟用浮動通知</string>
|
<string name="enable_heads_up_summary">對新安裝或已更新的模塊啟用浮動通知</string>
|
||||||
<string name="no_backup_found">找不到備份</string>
|
|
||||||
<string name="no_enabled_modules">沒有任何已啟用的模塊</string>
|
<string name="no_enabled_modules">沒有任何已啟用的模塊</string>
|
||||||
|
|
||||||
<string name="share">分享</string>
|
<string name="share">分享</string>
|
||||||
|
|
@ -195,7 +192,6 @@
|
||||||
|
|
||||||
<string name="changelog">更新日誌</string>
|
<string name="changelog">更新日誌</string>
|
||||||
<string name="scroll_top">滾動到頂部</string>
|
<string name="scroll_top">滾動到頂部</string>
|
||||||
<string name="file_is_empty">文件為空</string>
|
|
||||||
<string name="install_with_su">靜默安裝(root)</string>
|
<string name="install_with_su">靜默安裝(root)</string>
|
||||||
<string name="install_with_su_summ">勾選後在 EdXposed Manager 內安裝模塊時將不再顯示安裝提示</string>
|
<string name="install_with_su_summ">勾選後在 EdXposed Manager 內安裝模塊時將不再顯示安裝提示</string>
|
||||||
<string name="module_saved">模塊已保存至 %1$s</string>
|
<string name="module_saved">模塊已保存至 %1$s</string>
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,6 @@
|
||||||
|
|
||||||
<!-- Logs tab -->
|
<!-- Logs tab -->
|
||||||
<string name="logs_save_failed">無法將日誌寫入 SD 卡: </string>
|
<string name="logs_save_failed">無法將日誌寫入 SD 卡: </string>
|
||||||
<string name="sdcard_not_writable">無法找到 SD 卡或不可寫入</string>
|
|
||||||
<string name="menuClearLog">立即清理日誌</string>
|
<string name="menuClearLog">立即清理日誌</string>
|
||||||
<string name="logs_cleared">日誌清理成功</string>
|
<string name="logs_cleared">日誌清理成功</string>
|
||||||
<string name="logs_clear_failed">無法清理日誌: </string>
|
<string name="logs_clear_failed">無法清理日誌: </string>
|
||||||
|
|
@ -156,7 +155,6 @@
|
||||||
<string name="download_could_not_read_file">無法讀取下載的檔案: %s</string>
|
<string name="download_could_not_read_file">無法讀取下載的檔案: %s</string>
|
||||||
<string name="download_no_valid_apk">下載的檔案並非有效的 APK (或存在相容性問題)</string>
|
<string name="download_no_valid_apk">下載的檔案並非有效的 APK (或存在相容性問題)</string>
|
||||||
<string name="download_incorrect_package_name">檔案包名不正確(下載: %1$s, 預期: %2$s)</string>
|
<string name="download_incorrect_package_name">檔案包名不正確(下載: %1$s, 預期: %2$s)</string>
|
||||||
<string name="download_running">正在下載</string>
|
|
||||||
|
|
||||||
<!-- RepoLoader -->
|
<!-- RepoLoader -->
|
||||||
<string name="repo_download_failed_http">下載 %1$s 失敗: %2$d(%3$s)</string>
|
<string name="repo_download_failed_http">下載 %1$s 失敗: %2$d(%3$s)</string>
|
||||||
|
|
@ -173,7 +171,6 @@
|
||||||
<string name="no_installed_modules">沒有任何已安裝的模組</string>
|
<string name="no_installed_modules">沒有任何已安裝的模組</string>
|
||||||
<string name="enable_heads_up">啟用浮動通知</string>
|
<string name="enable_heads_up">啟用浮動通知</string>
|
||||||
<string name="enable_heads_up_summary">對新安裝或已更新的模組啟用浮動通知</string>
|
<string name="enable_heads_up_summary">對新安裝或已更新的模組啟用浮動通知</string>
|
||||||
<string name="no_backup_found">找不到備份</string>
|
|
||||||
<string name="no_enabled_modules">沒有任何已啟用的模組</string>
|
<string name="no_enabled_modules">沒有任何已啟用的模組</string>
|
||||||
|
|
||||||
<string name="share">分享</string>
|
<string name="share">分享</string>
|
||||||
|
|
@ -195,7 +192,6 @@
|
||||||
|
|
||||||
<string name="changelog">更新日誌</string>
|
<string name="changelog">更新日誌</string>
|
||||||
<string name="scroll_top">滾動到頂部</string>
|
<string name="scroll_top">滾動到頂部</string>
|
||||||
<string name="file_is_empty">檔案為空</string>
|
|
||||||
<string name="install_with_su">靜默安裝(root)</string>
|
<string name="install_with_su">靜默安裝(root)</string>
|
||||||
<string name="install_with_su_summ">勾選後在 EdXposed Manager 內安裝模組時將不再顯示安裝提示</string>
|
<string name="install_with_su_summ">勾選後在 EdXposed Manager 內安裝模組時將不再顯示安裝提示</string>
|
||||||
<string name="module_saved">模組已儲存至 %1$s</string>
|
<string name="module_saved">模組已儲存至 %1$s</string>
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,6 @@
|
||||||
|
|
||||||
<!-- Logs tab -->
|
<!-- Logs tab -->
|
||||||
<string name="logs_save_failed">Could not write log to SD card:</string>
|
<string name="logs_save_failed">Could not write log to SD card:</string>
|
||||||
<string name="sdcard_not_writable">SD card not found or not writable</string>
|
|
||||||
<string name="menuClearLog">Clear log now</string>
|
<string name="menuClearLog">Clear log now</string>
|
||||||
<string name="logs_cleared">Log successfully cleared.</string>
|
<string name="logs_cleared">Log successfully cleared.</string>
|
||||||
<string name="logs_clear_failed">Could not clear the log:</string>
|
<string name="logs_clear_failed">Could not clear the log:</string>
|
||||||
|
|
@ -158,8 +157,6 @@
|
||||||
<string name="download_could_not_read_file">Could not read downloaded file: %s</string>
|
<string name="download_could_not_read_file">Could not read downloaded file: %s</string>
|
||||||
<string name="download_no_valid_apk">Downloaded file is not a valid APK (or incompatible)</string>
|
<string name="download_no_valid_apk">Downloaded file is not a valid APK (or incompatible)</string>
|
||||||
<string name="download_incorrect_package_name">Package name is incorrect (downloaded: %1$s, expected: %2$s)</string>
|
<string name="download_incorrect_package_name">Package name is incorrect (downloaded: %1$s, expected: %2$s)</string>
|
||||||
<string name="download_running">Download is running</string>
|
|
||||||
<string name="download_progress" translatable="false">%1$,d / %2$,d kB</string>
|
|
||||||
|
|
||||||
<!-- RepoLoader -->
|
<!-- RepoLoader -->
|
||||||
<string name="repo_download_failed_http">Downloading %1$s failed: %2$d (%3$s)</string>
|
<string name="repo_download_failed_http">Downloading %1$s failed: %2$d (%3$s)</string>
|
||||||
|
|
@ -176,7 +173,6 @@
|
||||||
<string name="no_installed_modules">There are no installed modules!</string>
|
<string name="no_installed_modules">There are no installed modules!</string>
|
||||||
<string name="enable_heads_up">Enable Heads-Up notification</string>
|
<string name="enable_heads_up">Enable Heads-Up notification</string>
|
||||||
<string name="enable_heads_up_summary">This option enables heads up notification on new/updated module</string>
|
<string name="enable_heads_up_summary">This option enables heads up notification on new/updated module</string>
|
||||||
<string name="no_backup_found">No backup found</string>
|
|
||||||
<string name="no_enabled_modules">There are no modules enabled</string>
|
<string name="no_enabled_modules">There are no modules enabled</string>
|
||||||
|
|
||||||
<string name="share">Share with…</string>
|
<string name="share">Share with…</string>
|
||||||
|
|
@ -198,7 +194,6 @@
|
||||||
|
|
||||||
<string name="changelog">Changelog</string>
|
<string name="changelog">Changelog</string>
|
||||||
<string name="scroll_top">Scroll to top</string>
|
<string name="scroll_top">Scroll to top</string>
|
||||||
<string name="file_is_empty">File is empty</string>
|
|
||||||
<string name="install_with_su">Silent installation (root)</string>
|
<string name="install_with_su">Silent installation (root)</string>
|
||||||
<string name="install_with_su_summ">If checked EdXposed Manager will install modules without a prompt</string>
|
<string name="install_with_su_summ">If checked EdXposed Manager will install modules without a prompt</string>
|
||||||
<string name="module_saved">Module saved at %1$s</string>
|
<string name="module_saved">Module saved at %1$s</string>
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,7 @@
|
||||||
<paths>
|
<paths>
|
||||||
<external-cache-path
|
<external-cache-path
|
||||||
name="downloads"
|
name="downloads"
|
||||||
path="downloads" />
|
path="/downloads" />
|
||||||
<external-path
|
|
||||||
name="download"
|
|
||||||
path="Download" />
|
|
||||||
<!--suppress AndroidElementNotAllowed -->
|
<!--suppress AndroidElementNotAllowed -->
|
||||||
<root-path
|
<root-path
|
||||||
name="log_legacy"
|
name="log_legacy"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue