diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 152a19e3b..cbbaf399b 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -19,7 +19,6 @@ - @@ -36,7 +35,6 @@ android:label="@string/app_name" android:largeHeap="true" android:networkSecurityConfig="@xml/network_security_config" - android:requestLegacyExternalStorage="true" android:roundIcon="@mipmap/ic_launcher_round" android:theme="@style/AppTheme"> diff --git a/app/src/main/java/com/hippo/ehviewer/AppConfig.java b/app/src/main/java/com/hippo/ehviewer/AppConfig.java index 8eb341fe3..698da29d4 100644 --- a/app/src/main/java/com/hippo/ehviewer/AppConfig.java +++ b/app/src/main/java/com/hippo/ehviewer/AppConfig.java @@ -53,7 +53,16 @@ public class AppConfig { @Nullable public static File getExternalAppDir() { if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { - File dir = new File(Environment.getExternalStorageDirectory(), APP_DIRNAME); + File dir = sContext.getExternalFilesDir(null); + return FileUtils.ensureDirectory(dir) ? dir : null; + } + return null; + } + + @Nullable + public static File getExternalMediaDir() { + if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { + File dir = new File(sContext.getExternalMediaDirs()[0], APP_DIRNAME); return FileUtils.ensureDirectory(dir) ? dir : null; } return null; @@ -64,6 +73,7 @@ public class AppConfig { */ @Nullable public static File getDirInExternalAppDir(String filename) { + File appFolder = getExternalAppDir(); if (appFolder != null) { File dir = new File(appFolder, filename); @@ -94,7 +104,7 @@ public class AppConfig { @Nullable public static File getExternalImageDir() { - return getDirInExternalAppDir(IMAGE); + return getExternalMediaDir(); } @Nullable @@ -102,11 +112,6 @@ public class AppConfig { return getDirInExternalAppDir(PARSE_ERROR); } - @Nullable - public static File getExternalLogcatDir() { - return getDirInExternalAppDir(LOGCAT); - } - @Nullable public static File getExternalDataDir() { return getDirInExternalAppDir(DATA); diff --git a/app/src/main/java/com/hippo/ehviewer/EhDB.java b/app/src/main/java/com/hippo/ehviewer/EhDB.java index f6584a723..85e66d286 100644 --- a/app/src/main/java/com/hippo/ehviewer/EhDB.java +++ b/app/src/main/java/com/hippo/ehviewer/EhDB.java @@ -20,6 +20,7 @@ import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; +import android.net.Uri; import android.util.Log; import androidx.annotation.NonNull; @@ -619,7 +620,7 @@ public class EhDB { return true; } - public static synchronized boolean exportDB(Context context, File file) { + public static synchronized boolean exportDB(Context context, Uri uri) { final String ehExportName = "eh.export.db"; // Delete old export db @@ -659,7 +660,7 @@ public class EhDB { OutputStream os = null; try { is = new FileInputStream(dbFile); - os = new FileOutputStream(file); + os = context.getContentResolver().openOutputStream(uri);; IOUtils.copy(is, os); return true; } catch (IOException e) { @@ -669,7 +670,6 @@ public class EhDB { IOUtils.closeQuietly(os); } // Delete failed file - file.delete(); return false; } finally { context.deleteDatabase(ehExportName); @@ -680,8 +680,23 @@ public class EhDB { * @param file The db file * @return error string, null for no error */ - public static synchronized String importDB(Context context, File file) { + public static synchronized String importDB(Context context, Uri uri) { try { + InputStream inputStream = context.getContentResolver().openInputStream(uri); + File file = File.createTempFile("importDatabase", ""); + FileOutputStream outputStream = new FileOutputStream(file); + byte[] buff = new byte[1024]; + int read; + if (inputStream != null) { + while ((read = inputStream.read(buff, 0, buff.length)) > 0) { + outputStream.write(buff, 0, read); + } + } else { + return context.getString(R.string.cant_read_the_file); + } + inputStream.close(); + outputStream.close(); + SQLiteDatabase db = SQLiteDatabase.openDatabase( file.getPath(), null, SQLiteDatabase.NO_LOCALIZED_COLLATORS); int newVersion = DaoMaster.SCHEMA_VERSION; diff --git a/app/src/main/java/com/hippo/ehviewer/preference/ExportDataPreference.java b/app/src/main/java/com/hippo/ehviewer/preference/ExportDataPreference.java deleted file mode 100644 index 940e3a6c9..000000000 --- a/app/src/main/java/com/hippo/ehviewer/preference/ExportDataPreference.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2019 Hippo Seven - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.hippo.ehviewer.preference; - -import android.content.Context; -import android.util.AttributeSet; -import android.widget.Toast; - -import androidx.annotation.NonNull; - -import com.hippo.ehviewer.AppConfig; -import com.hippo.ehviewer.EhDB; -import com.hippo.ehviewer.GetText; -import com.hippo.ehviewer.R; -import com.hippo.util.ReadableTime; - -import java.io.File; - -public class ExportDataPreference extends TaskPreference { - - public ExportDataPreference(Context context) { - super(context); - } - - public ExportDataPreference(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public ExportDataPreference(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @NonNull - @Override - protected Task onCreateTask() { - return new ExportDataTask(getContext()); - } - - private static class ExportDataTask extends Task { - - public ExportDataTask(@NonNull Context context) { - super(context); - } - - @Override - protected Object doInBackground(Void... voids) { - File dir = AppConfig.getExternalDataDir(); - if (dir != null) { - File file = new File(dir, ReadableTime.getFilenamableTime(System.currentTimeMillis()) + ".db"); - if (EhDB.exportDB(getApplication(), file)) { - return file; - } - } - return null; - } - - @Override - protected void onPostExecute(Object o) { - Toast.makeText(getApplication(), - (o instanceof File) - ? GetText.getString(R.string.settings_advanced_export_data_to, ((File) o).getPath()) - : GetText.getString(R.string.settings_advanced_export_data_failed), - Toast.LENGTH_SHORT).show(); - super.onPostExecute(o); - } - } -} diff --git a/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java index 6f17c538b..db99a76a8 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java @@ -330,7 +330,6 @@ public final class MainActivity extends StageActivity } if (savedInstanceState == null) { - onInit(); CommonOperations.checkUpdate(this, false); checkDownloadLocation(); if (Settings.getCellularNetworkWarning()) { @@ -362,12 +361,6 @@ public final class MainActivity extends StageActivity } } - private void onInit() { - // Check permission - PermissionRequester.request(this, Manifest.permission.WRITE_EXTERNAL_STORAGE, - getString(R.string.write_rationale), PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE); - } - private void onRestore(Bundle savedInstanceState) { mNavCheckedItem = savedInstanceState.getInt(KEY_NAV_CHECKED_ITEM); } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/SettingsActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/SettingsActivity.java index 010b4b320..9b7fce500 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/SettingsActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/SettingsActivity.java @@ -38,15 +38,6 @@ public final class SettingsActivity extends EhActivity { private static final int REQUEST_CODE_FRAGMENT = 0; - private static final String[] ENTRY_FRAGMENTS = { - EhFragment.class.getName(), - ReadFragment.class.getName(), - DownloadFragment.class.getName(), - AdvancedFragment.class.getName(), - AboutFragment.class.getName(), - PrivacyFragment.class.getName(), - }; - @Override protected int getThemeResId(int theme) { return R.style.AppTheme_Settings; diff --git a/app/src/main/java/com/hippo/ehviewer/ui/fragment/AdvancedFragment.java b/app/src/main/java/com/hippo/ehviewer/ui/fragment/AdvancedFragment.java index 71bc2c2fd..a91fcc418 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/fragment/AdvancedFragment.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/fragment/AdvancedFragment.java @@ -18,18 +18,23 @@ package com.hippo.ehviewer.ui.fragment; import android.app.Activity; import android.content.Context; +import android.content.Intent; import android.content.res.Resources; +import android.net.Uri; import android.os.Bundle; +import android.os.Looper; import android.widget.Toast; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.preference.Preference; +import androidx.preference.PreferenceGroup; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.hippo.ehviewer.AppConfig; import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.EhDB; +import com.hippo.ehviewer.GetText; import com.hippo.ehviewer.R; import com.hippo.util.LogCat; import com.hippo.util.ReadableTime; @@ -41,10 +46,15 @@ import java.util.Arrays; public class AdvancedFragment extends PreferenceFragmentCompat implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { + private static final int REQUEST_CODE_EXPORT = 1; + private static final int REQUEST_CODE_IMPORT = 2; + private static final int REQUEST_DUMP_LOGCAT = 3; + private static final String KEY_DUMP_LOGCAT = "dump_logcat"; private static final String KEY_CLEAR_MEMORY_CACHE = "clear_memory_cache"; private static final String KEY_APP_LANGUAGE = "app_language"; private static final String KEY_IMPORT_DATA = "import_data"; + private static final String KEY_EXPORT_DATA = "export_data"; private static void importData(final Context context) { final File dir = AppConfig.getExternalDataDir(); @@ -60,11 +70,11 @@ public class AdvancedFragment extends PreferenceFragmentCompat Arrays.sort(files); new MaterialAlertDialogBuilder(context).setItems(files, (dialog, which) -> { File file = new File(dir, files[which]); - String error = EhDB.importDB(context, file); - if (null == error) { - error = context.getString(R.string.settings_advanced_import_data_successfully); - } - Toast.makeText(context, error, Toast.LENGTH_SHORT).show(); + //String error = EhDB.importDB(context, file); + //if (null == error) { + // error = context.getString(R.string.settings_advanced_import_data_successfully); + //} + //Toast.makeText(context, error, Toast.LENGTH_SHORT).show(); }).show(); } @@ -76,10 +86,12 @@ public class AdvancedFragment extends PreferenceFragmentCompat Preference clearMemoryCache = findPreference(KEY_CLEAR_MEMORY_CACHE); Preference appLanguage = findPreference(KEY_APP_LANGUAGE); Preference importData = findPreference(KEY_IMPORT_DATA); + Preference exportData = findPreference(KEY_EXPORT_DATA); dumpLogcat.setOnPreferenceClickListener(this); clearMemoryCache.setOnPreferenceClickListener(this); importData.setOnPreferenceClickListener(this); + exportData.setOnPreferenceClickListener(this); appLanguage.setOnPreferenceChangeListener(this); } @@ -93,31 +105,108 @@ public class AdvancedFragment extends PreferenceFragmentCompat public boolean onPreferenceClick(Preference preference) { String key = preference.getKey(); if (KEY_DUMP_LOGCAT.equals(key)) { - boolean ok; - File file = null; - File dir = AppConfig.getExternalLogcatDir(); - if (dir != null) { - file = new File(dir, "logcat-" + ReadableTime.getFilenamableTime(System.currentTimeMillis()) + ".txt"); - ok = LogCat.save(file); - } else { - ok = false; - } - Resources resources = getResources(); - Toast.makeText(getActivity(), - ok ? resources.getString(R.string.settings_advanced_dump_logcat_to, file.getPath()) : - resources.getString(R.string.settings_advanced_dump_logcat_failed), Toast.LENGTH_SHORT).show(); + Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_TITLE, "logcat-" + ReadableTime.getFilenamableTime(System.currentTimeMillis()) + ".txt"); + startActivityForResult(intent, REQUEST_DUMP_LOGCAT); return true; } else if (KEY_CLEAR_MEMORY_CACHE.equals(key)) { ((EhApplication) getActivity().getApplication()).clearMemoryCache(); Runtime.getRuntime().gc(); } else if (KEY_IMPORT_DATA.equals(key)) { - importData(getActivity()); - getActivity().setResult(Activity.RESULT_OK); + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("application/x-sqlite3"); + startActivityForResult(intent, REQUEST_CODE_IMPORT); + return true; + } else if (KEY_EXPORT_DATA.equals(key)) { + Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("application/x-sqlite3"); + intent.putExtra(Intent.EXTRA_TITLE, ReadableTime.getFilenamableTime(System.currentTimeMillis()) + ".db"); + startActivityForResult(intent, REQUEST_CODE_EXPORT); return true; } return false; } + @Override + protected void onActivityResult(PreferenceGroup group, int requestCode, int resultCode, Intent data) { + if (resultCode != Activity.RESULT_OK) { + return; + } + if (requestCode == REQUEST_CODE_EXPORT) { + if (data != null) { + Uri uri = data.getData(); + if (uri != null) { + try { + AlertDialog alertDialog = new MaterialAlertDialogBuilder(requireActivity()) + .setCancelable(false) + .setView(R.layout.preference_dialog_task) + .show(); + new Thread(() -> { + boolean success = EhDB.exportDB(requireActivity(), uri); + requireActivity().runOnUiThread(() -> { + if (alertDialog.isShowing()) { + alertDialog.dismiss(); + } + Toast.makeText(requireContext(), + (success) + ? GetText.getString(R.string.settings_advanced_export_data_to, uri.toString()) + : GetText.getString(R.string.settings_advanced_export_data_failed), + Toast.LENGTH_SHORT).show(); + }); + }).start(); + } catch (Exception e) { + GetText.getString(R.string.settings_advanced_export_data_failed); + } + } + } + } else if (requestCode == REQUEST_CODE_IMPORT) { + if (data != null) { + Uri uri = data.getData(); + if (uri != null) { + try { + AlertDialog alertDialog = new MaterialAlertDialogBuilder(requireActivity()) + .setCancelable(false) + .setView(R.layout.preference_dialog_task) + .show(); + new Thread(() -> { + final String error = EhDB.importDB(requireActivity(), uri); + requireActivity().runOnUiThread(() -> { + if (alertDialog.isShowing()) { + alertDialog.dismiss(); + } + if (null == error) { + Toast.makeText(requireContext(), getString(R.string.settings_advanced_import_data_successfully), Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(requireContext(), error, Toast.LENGTH_SHORT).show(); + } + }); + }).start(); + } catch (Exception e) { + Toast.makeText(requireContext(), e.getLocalizedMessage(), Toast.LENGTH_SHORT).show(); + } + } + } + } else if (requestCode == REQUEST_DUMP_LOGCAT) { + if (data != null) { + Uri uri = data.getData(); + if (uri != null) { + try { + boolean ok = LogCat.save(requireActivity().getContentResolver().openOutputStream(uri)); + Toast.makeText(getActivity(), + ok ? getString(R.string.settings_advanced_dump_logcat_to, uri.toString()) : + getString(R.string.settings_advanced_dump_logcat_failed), Toast.LENGTH_SHORT).show(); + }catch (Exception e){ + Toast.makeText(getActivity(), getString(R.string.settings_advanced_dump_logcat_failed), Toast.LENGTH_SHORT).show();} + } + } + } + super.onActivityResult(group, requestCode, resultCode, data); + } + @Override public boolean onPreferenceChange(Preference preference, Object newValue) { String key = preference.getKey(); diff --git a/app/src/main/java/com/hippo/util/LogCat.java b/app/src/main/java/com/hippo/util/LogCat.java index b49f421a9..07020081d 100644 --- a/app/src/main/java/com/hippo/util/LogCat.java +++ b/app/src/main/java/com/hippo/util/LogCat.java @@ -16,26 +16,20 @@ package com.hippo.util; -import com.hippo.yorozuya.FileUtils; import com.hippo.yorozuya.IOUtils; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; public final class LogCat { private LogCat() { } - public static boolean save(File file) { - if (!FileUtils.ensureFile(file)) { - return false; - } - + public static boolean save(OutputStream outputStream) { try { Process p = Runtime.getRuntime().exec("logcat -d"); - IOUtils.copy(p.getInputStream(), new FileOutputStream(file)); + IOUtils.copy(p.getInputStream(), outputStream); return true; } catch (IOException e) { e.printStackTrace(); diff --git a/app/src/main/res/xml/advanced_settings.xml b/app/src/main/res/xml/advanced_settings.xml index 2cdff6065..cd5af2a4c 100644 --- a/app/src/main/res/xml/advanced_settings.xml +++ b/app/src/main/res/xml/advanced_settings.xml @@ -89,7 +89,7 @@ app:activity="com.hippo.ehviewer.ui.HostsActivity" app:iconSpaceReserved="false" /> -