New log activity

This commit is contained in:
NekoInverter 2020-02-05 21:13:25 +08:00
parent d43eea7ebe
commit 0549f37277
13 changed files with 263 additions and 180 deletions

View File

@ -16,7 +16,6 @@ import android.widget.Button;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -24,6 +23,7 @@ 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;
@ -206,7 +206,7 @@ public class BaseAdvancedInstaller extends Fragment {
new Handler().postDelayed(() -> mClickedButton.performClick(), 500); new Handler().postDelayed(() -> mClickedButton.performClick(), 500);
} }
} else { } else {
Toast.makeText(getActivity(), R.string.permissionNotGranted, Toast.LENGTH_LONG).show(); Snackbar.make(getActivity().findViewById(R.id.snackbar), R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show();
} }
} }
} }

View File

@ -1,6 +1,7 @@
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.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@ -14,12 +15,13 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.fragment.app.ListFragment; import androidx.fragment.app.ListFragment;
import com.google.android.material.snackbar.Snackbar;
import org.meowcat.edxposed.manager.repo.Module; import org.meowcat.edxposed.manager.repo.Module;
import org.meowcat.edxposed.manager.repo.ModuleVersion; import org.meowcat.edxposed.manager.repo.ModuleVersion;
import org.meowcat.edxposed.manager.repo.ReleaseType; import org.meowcat.edxposed.manager.repo.ReleaseType;
@ -40,6 +42,7 @@ import static org.meowcat.edxposed.manager.XposedApp.WRITE_EXTERNAL_PERMISSION;
public class DownloadDetailsVersionsFragment extends ListFragment { public class DownloadDetailsVersionsFragment extends ListFragment {
private DownloadDetailsActivity mActivity; private DownloadDetailsActivity mActivity;
private View rootView;
@Override @Override
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
@ -48,6 +51,7 @@ public class DownloadDetailsVersionsFragment extends ListFragment {
if (mActivity == null) { if (mActivity == null) {
return; return;
} }
rootView = mActivity.findViewById(R.id.snackbar);
Module module = mActivity.getModule(); Module module = mActivity.getModule();
if (module == null) if (module == null)
return; return;
@ -95,7 +99,7 @@ public class DownloadDetailsVersionsFragment extends ListFragment {
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
DownloadView.mClickedButton.performClick(); DownloadView.mClickedButton.performClick();
} else { } else {
Toast.makeText(getActivity(), R.string.permissionNotGranted, Toast.LENGTH_LONG).show(); Snackbar.make(rootView, R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show();
} }
} }
} }
@ -120,7 +124,7 @@ public class DownloadDetailsVersionsFragment extends ListFragment {
@Override @Override
public void onDownloadFinished(Context context, DownloadsUtil.DownloadInfo info) { public void onDownloadFinished(Context context, DownloadsUtil.DownloadInfo info) {
File localFile = new File(info.localFilename); File localFile = new File(info.localFilename);
View rootView = ((Activity) context).findViewById(R.id.snackbar);
if (!localFile.isFile()) if (!localFile.isFile())
return; return;
@ -128,12 +132,12 @@ public class DownloadDetailsVersionsFragment extends ListFragment {
try { try {
String actualMd5Sum = HashUtil.md5(localFile); String actualMd5Sum = HashUtil.md5(localFile);
if (!moduleVersion.md5sum.equals(actualMd5Sum)) { if (!moduleVersion.md5sum.equals(actualMd5Sum)) {
Toast.makeText(context, context.getString(R.string.download_md5sum_incorrect, actualMd5Sum, moduleVersion.md5sum), Toast.LENGTH_LONG).show(); Snackbar.make(rootView, context.getString(R.string.download_md5sum_incorrect, actualMd5Sum, moduleVersion.md5sum), Snackbar.LENGTH_LONG).show();
DownloadsUtil.removeById(context, info.id); DownloadsUtil.removeById(context, info.id);
return; return;
} }
} catch (Exception e) { } catch (Exception e) {
Toast.makeText(context, context.getString(R.string.download_could_not_read_file, e.getMessage()), Toast.LENGTH_LONG).show(); Snackbar.make(rootView, context.getString(R.string.download_could_not_read_file, e.getMessage()), Snackbar.LENGTH_LONG).show();
DownloadsUtil.removeById(context, info.id); DownloadsUtil.removeById(context, info.id);
return; return;
} }
@ -143,13 +147,13 @@ public class DownloadDetailsVersionsFragment extends ListFragment {
PackageInfo packageInfo = pm.getPackageArchiveInfo(info.localFilename, 0); PackageInfo packageInfo = pm.getPackageArchiveInfo(info.localFilename, 0);
if (packageInfo == null) { if (packageInfo == null) {
Toast.makeText(context, R.string.download_no_valid_apk, Toast.LENGTH_LONG).show(); Snackbar.make(rootView, R.string.download_no_valid_apk, Snackbar.LENGTH_LONG).show();
DownloadsUtil.removeById(context, info.id); DownloadsUtil.removeById(context, info.id);
return; return;
} }
if (!packageInfo.packageName.equals(moduleVersion.module.packageName)) { if (!packageInfo.packageName.equals(moduleVersion.module.packageName)) {
Toast.makeText(context, context.getString(R.string.download_incorrect_package_name, packageInfo.packageName, moduleVersion.module.packageName), Toast.LENGTH_LONG).show(); Snackbar.make(rootView, context.getString(R.string.download_incorrect_package_name, packageInfo.packageName, moduleVersion.module.packageName), Snackbar.LENGTH_LONG).show();
DownloadsUtil.removeById(context, info.id); DownloadsUtil.removeById(context, info.id);
return; return;
} }

View File

@ -7,17 +7,17 @@ import android.content.Intent;
import android.content.pm.PackageManager; 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.Environment;
import android.os.Handler; import android.os.Handler;
import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.HorizontalScrollView;
import android.widget.ScrollView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -25,29 +25,33 @@ import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.content.FileProvider; import androidx.core.content.FileProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.tabs.TabLayout;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.Objects; import java.util.Scanner;
public class LogsActivity extends BaseActivity { public class LogsActivity extends BaseActivity {
private boolean errorLog = false; private boolean allLog = false;
private File mFileErrorLog = new File(XposedApp.BASE_DIR + "log/error.log"); private File mFileErrorLog = new File(XposedApp.BASE_DIR + "log/error.log");
private File mFileErrorLogOld = new File( private File mFileErrorLogOld = new File(
XposedApp.BASE_DIR + "log/error.log.old"); XposedApp.BASE_DIR + "log/error.log.old");
private File mFileErrorLogError = new File(XposedApp.BASE_DIR + "log/all.log"); private File mFileAllLog = new File(XposedApp.BASE_DIR + "log/all.log");
private File mFileErrorLogOldError = new File(XposedApp.BASE_DIR + "log/all.log.old"); private File mFileAllLogOld = new File(XposedApp.BASE_DIR + "log/all.log.old");
private TextView mTxtLog;
private ScrollView mSVLog;
private HorizontalScrollView mHSVLog;
private MenuItem mClickedMenuItem = null; private MenuItem mClickedMenuItem = null;
private LogsAdapter mAdapter;
private RecyclerView mListView;
private Handler handler = new Handler();
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
@ -61,10 +65,6 @@ public class LogsActivity extends BaseActivity {
bar.setDisplayHomeAsUpEnabled(true); bar.setDisplayHomeAsUpEnabled(true);
} }
setupWindowInsets(); setupWindowInsets();
mTxtLog = findViewById(R.id.txtLog);
mTxtLog.setTextIsSelectable(true);
mSVLog = findViewById(R.id.svLog);
mHSVLog = findViewById(R.id.hsvLog);
if (!XposedApp.getPreferences().getBoolean("hide_logcat_warning", false)) { if (!XposedApp.getPreferences().getBoolean("hide_logcat_warning", false)) {
@SuppressLint("InflateParams") final View dontShowAgainView = getLayoutInflater().inflate(R.layout.dialog_install_warning, null); @SuppressLint("InflateParams") final View dontShowAgainView = getLayoutInflater().inflate(R.layout.dialog_install_warning, null);
@ -83,6 +83,29 @@ public class LogsActivity extends BaseActivity {
.setCancelable(false) .setCancelable(false)
.show(); .show();
} }
mAdapter = new LogsAdapter();
mListView = findViewById(R.id.recyclerView);
mListView.setAdapter(mAdapter);
mListView.setLayoutManager(new LinearLayoutManager(this));
TabLayout tabLayout = findViewById(R.id.sliding_tabs);
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
allLog = tab.getPosition() != 0;
reloadErrorLog();
scrollDown();
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
} }
@Override @Override
@ -101,17 +124,6 @@ public class LogsActivity extends BaseActivity {
public boolean onOptionsItemSelected(@NonNull MenuItem item) { public boolean onOptionsItemSelected(@NonNull MenuItem item) {
mClickedMenuItem = item; mClickedMenuItem = item;
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.menu_logs:
item.setChecked(true);
errorLog = false;
reloadErrorLog();
break;
case R.id.menu_logs_err:
item.setChecked(true);
errorLog = true;
reloadErrorLog();
scrollDown();
break;
case R.id.menu_scroll_top: case R.id.menu_scroll_top:
scrollTop(); scrollTop();
break; break;
@ -138,36 +150,32 @@ public class LogsActivity extends BaseActivity {
} }
private void scrollTop() { private void scrollTop() {
mSVLog.post(() -> mSVLog.scrollTo(0, 0)); mListView.smoothScrollToPosition(0);
mHSVLog.post(() -> mHSVLog.scrollTo(0, 0));
} }
private void scrollDown() { private void scrollDown() {
mSVLog.post(() -> mSVLog.scrollTo(0, mTxtLog.getHeight())); mListView.smoothScrollToPosition(mAdapter.getItemCount() - 1);
mHSVLog.post(() -> mHSVLog.scrollTo(0, 0));
} }
private void reloadErrorLog() { private void reloadErrorLog() {
new LogsReader().execute(errorLog ? mFileErrorLogError : mFileErrorLog); new LogsReader().execute(allLog ? mFileAllLog : mFileErrorLog);
mSVLog.post(() -> mSVLog.scrollTo(0, mTxtLog.getHeight()));
mHSVLog.post(() -> mHSVLog.scrollTo(0, 0));
} }
private void clear() { private void clear() {
try { try {
new FileOutputStream(errorLog ? mFileErrorLogError : mFileErrorLog).close(); new FileOutputStream(allLog ? mFileAllLog : mFileErrorLog).close();
(errorLog ? mFileErrorLogOldError : mFileErrorLogOld).delete(); //noinspection ResultOfMethodCallIgnored
mTxtLog.setText(R.string.log_is_empty); (allLog ? mFileAllLogOld : mFileErrorLogOld).delete();
Toast.makeText(this, R.string.logs_cleared, mAdapter.setEmpty();
Toast.LENGTH_SHORT).show(); Snackbar.make(findViewById(R.id.snackbar), R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
reloadErrorLog(); reloadErrorLog();
} catch (IOException e) { } catch (IOException e) {
Toast.makeText(this, getResources().getString(R.string.logs_clear_failed) + "n" + e.getMessage(), Toast.LENGTH_LONG).show(); Snackbar.make(findViewById(R.id.snackbar), getResources().getString(R.string.logs_clear_failed) + "n" + e.getMessage(), Snackbar.LENGTH_LONG).show();
} }
} }
private void send() { private void send() {
Uri uri = FileProvider.getUriForFile(this, "org.meowcat.edxposed.manager.fileprovider", errorLog ? mFileErrorLogError : mFileErrorLog); Uri uri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".fileprovider", allLog ? mFileAllLog : mFileErrorLog);
Intent sendIntent = new Intent(); Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND); sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_STREAM, uri); sendIntent.putExtra(Intent.EXTRA_STREAM, uri);
@ -187,20 +195,20 @@ public class LogsActivity extends BaseActivity {
new Handler().postDelayed(() -> onOptionsItemSelected(mClickedMenuItem), 500); new Handler().postDelayed(() -> onOptionsItemSelected(mClickedMenuItem), 500);
} }
} else { } else {
Toast.makeText(this, R.string.permissionNotGranted, Toast.LENGTH_LONG).show(); Snackbar.make(findViewById(R.id.snackbar), R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show();
} }
} }
} }
@SuppressLint("DefaultLocale") @SuppressLint("DefaultLocale")
private void save() { private void save() {
if (ActivityCompat.checkSelfPermission(Objects.requireNonNull(this), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { 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); requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, XposedApp.WRITE_EXTERNAL_PERMISSION);
return; return;
} }
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
Toast.makeText(this, R.string.sdcard_not_writable, Toast.LENGTH_LONG).show(); Snackbar.make(findViewById(R.id.snackbar), R.string.sdcard_not_writable, Snackbar.LENGTH_LONG).show();
return; return;
} }
@ -214,7 +222,7 @@ public class LogsActivity extends BaseActivity {
File targetFile = new File(XposedApp.createFolder(), filename); File targetFile = new File(XposedApp.createFolder(), filename);
try { try {
FileInputStream in = new FileInputStream(errorLog ? mFileErrorLogError : mFileErrorLog); FileInputStream in = new FileInputStream(allLog ? mFileAllLog : mFileErrorLog);
FileOutputStream out = new FileOutputStream(targetFile); FileOutputStream out = new FileOutputStream(targetFile);
byte[] buffer = new byte[1024]; byte[] buffer = new byte[1024];
int len; int len;
@ -224,93 +232,119 @@ public class LogsActivity extends BaseActivity {
in.close(); in.close();
out.close(); out.close();
Toast.makeText(this, targetFile.toString(), Snackbar.make(findViewById(R.id.snackbar), targetFile.toString(), Snackbar.LENGTH_LONG).show();
Toast.LENGTH_LONG).show();
} catch (IOException e) { } catch (IOException e) {
Toast.makeText(this, getResources().getString(R.string.logs_save_failed) + "\n" + e.getMessage(), Toast.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, String> { private class LogsReader extends AsyncTask<File, Integer, ArrayList<String>> {
private static final int MAX_LOG_SIZE = 1000 * 1024; // 1000 KB
private ProgressDialog mProgressDialog; private ProgressDialog mProgressDialog;
private Runnable mRunnable = new Runnable() {
private long skipLargeFile(BufferedReader is, long length) throws IOException { @Override
if (length < MAX_LOG_SIZE) public void run() {
return 0; mProgressDialog.show();
long skipped = length - MAX_LOG_SIZE;
long yetToSkip = skipped;
do {
yetToSkip -= is.skip(yetToSkip);
} while (yetToSkip > 0);
int c;
do {
c = is.read();
if (c == -1)
break;
skipped++;
} while (c != '\n');
return skipped;
} }
};
@Override @Override
protected void onPreExecute() { protected void onPreExecute() {
mTxtLog.setText("");
mProgressDialog = new ProgressDialog(LogsActivity.this); mProgressDialog = new ProgressDialog(LogsActivity.this);
mProgressDialog.setMessage(getString(R.string.loading)); mProgressDialog.setMessage(getString(R.string.loading));
mProgressDialog.setProgress(0); mProgressDialog.setProgress(0);
mProgressDialog.show(); handler.postDelayed(mRunnable, 500);
} }
@Override @Override
protected String doInBackground(File... log) { protected ArrayList<String> doInBackground(File... log) {
Thread.currentThread().setPriority(Thread.NORM_PRIORITY + 2); Thread.currentThread().setPriority(Thread.NORM_PRIORITY + 2);
StringBuilder llog = new StringBuilder(15 * 10 * 1024); ArrayList<String> logs = new ArrayList<>();
if (XposedApp.getPreferences().getBoolean("disable_verbose_log", false) && allLog) {
if (XposedApp.getPreferences().getBoolean( logs.add(LogsActivity.this.getResources().getString(R.string.logs_verbose_disabled));
"disable_verbose_log", false) && errorLog) { return logs;
llog.append(LogsActivity.this.getResources().getString(R.string.logs_verbose_disabled));
return llog.toString();
} }
try { try {
File logfile = log[0]; File logfile = log[0];
BufferedReader br; try (Scanner scanner = new Scanner(logfile)) {
br = new BufferedReader(new FileReader(logfile)); while (scanner.hasNextLine()) {
long skipped = skipLargeFile(br, logfile.length()); logs.add(scanner.nextLine());
if (skipped > 0) {
llog.append(LogsActivity.this.getResources().getString(R.string.logs_too_long));
llog.append("\n-----------------\n");
} }
char[] temp = new char[1024];
int read;
while ((read = br.read(temp)) > 0) {
llog.append(temp, 0, read);
} }
br.close(); return logs;
} catch (IOException e) { } catch (IOException e) {
llog.append(LogsActivity.this.getResources().getString(R.string.logs_cannot_read)); logs.add(LogsActivity.this.getResources().getString(R.string.logs_cannot_read));
llog.append(e.getMessage()); logs.addAll(Arrays.asList(e.getMessage().split("\n")));
} }
return llog.toString(); return logs;
} }
@Override @Override
protected void onPostExecute(String llog) { protected void onPostExecute(ArrayList<String> logs) {
if (logs.size() == 0) {
mAdapter.setEmpty();
} else {
mAdapter.setLogs(logs);
}
handler.removeCallbacks(mRunnable);//It loaded so fast that no need to show progress
if (mProgressDialog.isShowing()){
mProgressDialog.dismiss(); mProgressDialog.dismiss();
mTxtLog.setText(llog); }
}
}
if (llog.length() == 0) private class LogsAdapter extends RecyclerView.Adapter<LogsAdapter.ViewHolder> {
mTxtLog.setText(R.string.log_is_empty); ArrayList<String> logs = new ArrayList<>();
@NonNull
@Override
public LogsAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_log, parent, false);
return new LogsAdapter.ViewHolder(v);
}
@Override
public void onBindViewHolder(@NonNull LogsAdapter.ViewHolder holder, int position) {
TextView view = holder.textView;
view.setText(logs.get(position));
view.measure(0, 0);
int desiredWidth = view.getMeasuredWidth();
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
layoutParams.width = desiredWidth;
if (mListView.getWidth() < desiredWidth) {
mListView.requestLayout();
} }
} }
void setLogs(ArrayList<String> logs) {
this.logs.clear();
this.logs.addAll(logs);
notifyDataSetChanged();
}
void setEmpty() {
logs.clear();
logs.add(getString(R.string.log_is_empty));
notifyDataSetChanged();
}
@Override
public int getItemCount() {
return logs.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView textView;
ViewHolder(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.log);
}
}
}
} }

View File

@ -24,7 +24,6 @@ import android.widget.Filter;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.Switch; import android.widget.Switch;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
@ -38,6 +37,8 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.material.snackbar.Snackbar;
import org.meowcat.edxposed.manager.repo.Module; import org.meowcat.edxposed.manager.repo.Module;
import org.meowcat.edxposed.manager.repo.ModuleVersion; import org.meowcat.edxposed.manager.repo.ModuleVersion;
import org.meowcat.edxposed.manager.repo.ReleaseType; import org.meowcat.edxposed.manager.repo.ReleaseType;
@ -189,7 +190,7 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
new Handler().postDelayed(() -> onOptionsItemSelected(mClickedMenuItem), 500); new Handler().postDelayed(() -> onOptionsItemSelected(mClickedMenuItem), 500);
} }
} else { } else {
Toast.makeText(this, R.string.permissionNotGranted, Toast.LENGTH_LONG).show(); Snackbar.make(findViewById(R.id.snackbar), R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show();
} }
} }
} }
@ -212,7 +213,7 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
} }
if (ModuleUtil.getInstance().getEnabledModules().isEmpty()) { if (ModuleUtil.getInstance().getEnabledModules().isEmpty()) {
Toast.makeText(this, getString(R.string.no_enabled_modules), Toast.LENGTH_SHORT).show(); Snackbar.make(findViewById(R.id.snackbar), R.string.no_enabled_modules, Snackbar.LENGTH_SHORT).show();
return false; return false;
} }
@ -230,21 +231,21 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
in.close(); in.close();
out.close(); out.close();
} catch (IOException e) { } catch (IOException e) {
Toast.makeText(this, getResources().getString(R.string.logs_save_failed) + "\n" + e.getMessage(), Toast.LENGTH_LONG).show(); Snackbar.make(findViewById(R.id.snackbar), getResources().getString(R.string.logs_save_failed) + "\n" + e.getMessage(), Snackbar.LENGTH_LONG).show();
return false; return false;
} }
Toast.makeText(this, enabledModulesPath.toString(), Toast.LENGTH_LONG).show(); 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)) { if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
Toast.makeText(this, R.string.sdcard_not_writable, Toast.LENGTH_LONG).show(); Snackbar.make(findViewById(R.id.snackbar), R.string.sdcard_not_writable, Snackbar.LENGTH_LONG).show();
return false; return false;
} }
Map<String, ModuleUtil.InstalledModule> installedModules = ModuleUtil.getInstance().getModules(); Map<String, ModuleUtil.InstalledModule> installedModules = ModuleUtil.getInstance().getModules();
if (installedModules.isEmpty()) { if (installedModules.isEmpty()) {
Toast.makeText(this, getString(R.string.no_installed_modules), Toast.LENGTH_SHORT).show(); Snackbar.make(findViewById(R.id.snackbar), R.string.no_installed_modules, Snackbar.LENGTH_SHORT).show();
return false; return false;
} }
@ -262,11 +263,11 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
fileOut.close(); fileOut.close();
} catch (IOException e) { } catch (IOException e) {
Toast.makeText(this, getResources().getString(R.string.logs_save_failed) + "\n" + e.getMessage(), Toast.LENGTH_LONG).show(); Snackbar.make(findViewById(R.id.snackbar), getResources().getString(R.string.logs_save_failed) + "\n" + e.getMessage(), Snackbar.LENGTH_LONG).show();
return false; return false;
} }
Toast.makeText(this, installedModulesPath.toString(), Toast.LENGTH_LONG).show(); 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); return importModules(installedModulesPath);
@ -279,15 +280,14 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
private boolean importModules(File path) { private boolean importModules(File path) {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
Toast.makeText(this, R.string.sdcard_not_writable, Toast.LENGTH_LONG).show(); Snackbar.make(findViewById(R.id.snackbar), R.string.sdcard_not_writable, Snackbar.LENGTH_LONG).show();
return false; return false;
} }
InputStream ips = null; InputStream ips = null;
RepoLoader repoLoader = RepoLoader.getInstance(); RepoLoader repoLoader = RepoLoader.getInstance();
List<Module> list = new ArrayList<>(); List<Module> list = new ArrayList<>();
if (!path.exists()) { if (!path.exists()) {
Toast.makeText(this, getString(R.string.no_backup_found), Snackbar.make(findViewById(R.id.snackbar), R.string.no_backup_found, Snackbar.LENGTH_LONG).show();
Toast.LENGTH_LONG).show();
return false; return false;
} }
try { try {
@ -297,8 +297,7 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
} }
if (path.length() == 0) { if (path.length() == 0) {
Toast.makeText(this, R.string.file_is_empty, Snackbar.make(findViewById(R.id.snackbar), R.string.file_is_empty, Snackbar.LENGTH_LONG).show();
Toast.LENGTH_LONG).show();
return false; return false;
} }
@ -311,15 +310,15 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
Module m = repoLoader.getModule(line); Module m = repoLoader.getModule(line);
if (m == null) { if (m == null) {
Toast.makeText(this, getString(R.string.download_details_not_found, Snackbar.make(findViewById(R.id.snackbar), getString(R.string.download_details_not_found,
line), Toast.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 (ActivityNotFoundException | IOException e) {
Toast.makeText(this, e.toString(), Toast.LENGTH_SHORT).show(); Snackbar.make(findViewById(R.id.snackbar), e.toString(), Snackbar.LENGTH_SHORT).show();
} }
for (final Module m : list) { for (final Module m : list) {
@ -397,7 +396,7 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
if (launchIntent != null) { if (launchIntent != null) {
startActivity(launchIntent); startActivity(launchIntent);
} else { } else {
Toast.makeText(this, getString(R.string.module_no_ui), Toast.LENGTH_LONG).show(); Snackbar.make(findViewById(R.id.snackbar), R.string.module_no_ui, Snackbar.LENGTH_LONG).show();
} }
return true; return true;
@ -473,7 +472,7 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
if (launchIntent != null) { if (launchIntent != null) {
startActivity(launchIntent); startActivity(launchIntent);
} else { } else {
Toast.makeText(this, getString(R.string.module_no_ui), Toast.LENGTH_LONG).show(); Snackbar.make(findViewById(R.id.snackbar), R.string.module_no_ui, Snackbar.LENGTH_LONG).show();
} }
} }
} else { } else {
@ -485,7 +484,7 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
if (launchIntent != null) { if (launchIntent != null) {
startActivity(launchIntent); startActivity(launchIntent);
} else { } else {
Toast.makeText(this, getString(R.string.module_no_ui), Toast.LENGTH_LONG).show(); Snackbar.make(findViewById(R.id.snackbar), R.string.module_no_ui, Snackbar.LENGTH_LONG).show();
} }
} }
} }

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<font
android:font="@font/exo_regular"
android:fontStyle="normal"
android:fontWeight="400"
app:font="@font/exo_regular"
app:fontStyle="normal"
app:fontWeight="400" />
<font
android:font="@font/exo_regular_italic"
android:fontStyle="italic"
android:fontWeight="400"
app:font="@font/exo_regular_italic"
app:fontStyle="italic"
app:fontWeight="400" />
<font
android:font="@font/exo_bold"
android:fontStyle="normal"
android:fontWeight="500"
app:font="@font/exo_bold"
app:fontStyle="normal"
app:fontWeight="500" />
<font
android:font="@font/exo_bold_italic"
android:fontStyle="italic"
android:fontWeight="500"
app:font="@font/exo_bold_italic"
app:fontStyle="italic"
app:fontWeight="500" />
</font-family>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,44 +1,59 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/snackbar" android:id="@+id/snackbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipChildren="false" android:clipChildren="false"
android:clipToPadding="false" android:clipToPadding="false">
tools:context=".LogsActivity">
<include layout="@layout/appbar_layout" /> <com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<RelativeLayout <androidx.appcompat.widget.Toolbar
android:id="@+id/container" android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?colorActionBar"
app:popupTheme="@style/AppTheme.PopupOverlay" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/sliding_tabs"
style="@style/Widget.MaterialComponents.TabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?colorActionBar"
app:tabMode="fixed">
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/nav_item_logs_err" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/nav_item_logs" />
</com.google.android.material.tabs.TabLayout>
</com.google.android.material.appbar.AppBarLayout>
<HorizontalScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:clipChildren="false" android:clipChildren="false"
android:clipToPadding="false" android:clipToPadding="false"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<ScrollView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/svLog" android:id="@+id/recyclerView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="match_parent"
tools:ignore="UselessParent"> android:clipChildren="false"
android:clipToPadding="false"
<HorizontalScrollView android:orientation="vertical" />
android:id="@+id/hsvLog"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/txtLog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:textIsSelectable="true" />
</HorizontalScrollView> </HorizontalScrollView>
</ScrollView> </androidx.coordinatorlayout.widget.CoordinatorLayout>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,8 @@
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/log"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:text=""
android:textAppearance="@style/AppearanceFoundation.Caption"
android:textSize="10sp" />

View File

@ -1,25 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto">
xmlns:tools="http://schemas.android.com/tools">
<item
android:icon="@drawable/ic_bug"
android:title="@string/Logs"
app:showAsAction="always"
tools:ignore="AlwaysShowAction">
<menu>
<group android:checkableBehavior="single">
<item
android:id="@+id/menu_logs"
android:checked="true"
android:title="@string/nav_item_logs" />
<item
android:id="@+id/menu_logs_err"
android:title="@string/nav_item_logs_err" />
</group>
</menu>
</item>
<item <item
android:id="@+id/menu_save" android:id="@+id/menu_save"
android:icon="@drawable/ic_save" android:icon="@drawable/ic_save"

View File

@ -16,6 +16,11 @@
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item> <item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style> </style>
<style name="AppearanceFoundation.Caption" parent="TextAppearance.AppCompat.Caption">
<item name="android:fontFamily">@font/exo</item>
<item name="android:textColor">?attr/colorOnSurface</item>
</style>
<style name="AppThemeMain" parent="AppTheme"> <style name="AppThemeMain" parent="AppTheme">
<item name="colorPrimaryDark">@color/colorBackground</item> <item name="colorPrimaryDark">@color/colorBackground</item>
</style> </style>