diff --git a/Clover/app/src/main/java/org/floens/chan/core/ChanPreferences.java b/Clover/app/src/main/java/org/floens/chan/core/ChanPreferences.java index a06cd526..d39bbdd5 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/ChanPreferences.java +++ b/Clover/app/src/main/java/org/floens/chan/core/ChanPreferences.java @@ -18,9 +18,12 @@ package org.floens.chan.core; import android.content.SharedPreferences; +import android.os.Environment; import org.floens.chan.ChanApplication; +import java.io.File; + public class ChanPreferences { private static SharedPreferences p() { return ChanApplication.getPreferences(); @@ -46,8 +49,24 @@ public class ChanPreferences { p().edit().putBoolean("preference_developer", developer).commit(); } - public static String getImageSaveDirectory() { - return "Clover"; + public static File getImageSaveDirectory() { + String path = p().getString("preference_image_save_location", null); + File file; + if (path == null) { + file = new File(Environment.getExternalStorageDirectory() + File.separator + "Clover"); + } else { + file = new File(path); + } + + return file; + } + + public static void setImageSaveDirectory(File file) { + p().edit().putString("preference_image_save_location", file.getAbsolutePath()).commit(); + } + + public static boolean getImageSaveOriginalFilename() { + return p().getBoolean("preference_image_save_original", false); } public static boolean getWatchEnabled() { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/AdvancedSettingsActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/AdvancedSettingsActivity.java index a04bd923..4cab2111 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/AdvancedSettingsActivity.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/activity/AdvancedSettingsActivity.java @@ -24,12 +24,19 @@ import android.preference.PreferenceActivity; import android.preference.PreferenceFragment; import org.floens.chan.R; +import org.floens.chan.core.ChanPreferences; +import org.floens.chan.ui.fragment.FolderPickFragment; +import org.floens.chan.utils.ThemeHelper; + +import java.io.File; public class AdvancedSettingsActivity extends PreferenceActivity { @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); + ThemeHelper.setTheme(this); + getFragmentManager().beginTransaction().replace(android.R.id.content, new AdvancedSettingsFragment()).commit(); } @@ -79,6 +86,32 @@ public class AdvancedSettingsActivity extends PreferenceActivity { return true; } }); + + reloadSavePath(); + final Preference saveLocation = findPreference("preference_image_save_location"); + saveLocation.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + File dir = ChanPreferences.getImageSaveDirectory(); + dir.mkdirs(); + + FolderPickFragment frag = FolderPickFragment.newInstance(new FolderPickFragment.FolderPickListener() { + @Override + public void folderPicked(File path) { + ChanPreferences.setImageSaveDirectory(path); + reloadSavePath(); + } + }, dir); + getActivity().getFragmentManager().beginTransaction().add(frag, null).commit(); + + return true; + } + }); + } + + private void reloadSavePath() { + Preference saveLocation = findPreference("preference_image_save_location"); + saveLocation.setSummary(ChanPreferences.getImageSaveDirectory().getAbsolutePath()); } private void updateSummary(ListPreference list, String value) { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/ImageViewActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/ImageViewActivity.java index fd701753..0a57d848 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/ImageViewActivity.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/activity/ImageViewActivity.java @@ -19,14 +19,15 @@ package org.floens.chan.ui.activity; import android.app.ActionBar; import android.app.Activity; -import android.net.Uri; import android.os.Bundle; import android.support.v4.view.ViewPager; +import android.text.TextUtils; import android.view.Menu; import android.view.MenuItem; import android.view.Window; import org.floens.chan.R; +import org.floens.chan.core.ChanPreferences; import org.floens.chan.core.model.Post; import org.floens.chan.ui.adapter.ImageViewAdapter; import org.floens.chan.ui.adapter.PostAdapter; @@ -166,20 +167,21 @@ public class ImageViewActivity extends Activity implements ViewPager.OnPageChang finish(); return true; } else if (item.getItemId() == R.id.action_download_album) { - List uris = new ArrayList<>(); - Post aPost = null; + List list = new ArrayList<>(); + + String name = "downloaded"; + String filename; for (Post post : adapter.getList()) { - uris.add(Uri.parse(post.imageUrl)); - aPost = post; + filename = (ChanPreferences.getImageSaveOriginalFilename() ? post.tim : post.filename) + "." + post.ext; + list.add(new ImageSaver.DownloadPair(post.imageUrl, filename)); + + name = post.board + "_" + post.resto; } - if (uris.size() > 0) { - String name = "downloaded"; - if (aPost != null) { - name = aPost.board + "_" + aPost.resto; + if (list.size() > 0) { + if (!TextUtils.isEmpty(name)) { + ImageSaver.saveAll(this, name, list); } - - ImageSaver.saveAll(this, name, uris); } return true; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/fragment/FolderPickFragment.java b/Clover/app/src/main/java/org/floens/chan/ui/fragment/FolderPickFragment.java new file mode 100644 index 00000000..887f48e6 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/ui/fragment/FolderPickFragment.java @@ -0,0 +1,196 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.floens.chan.ui.fragment; + +import android.app.DialogFragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.FrameLayout; +import android.widget.ListView; +import android.widget.TextView; + +import org.floens.chan.R; +import org.floens.chan.utils.ThemeHelper; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class FolderPickFragment extends DialogFragment { + private TextView statusPath; + private ListView listView; + private ArrayAdapter adapter; + boolean hasParent = false; + + private FolderPickListener listener; + private File currentPath; + private List directories; + private FrameLayout okButton; + private TextView okButtonIcon; + + public static FolderPickFragment newInstance(FolderPickListener listener, File startingPath) { + FolderPickFragment fragment = new FolderPickFragment(); + fragment.listener = listener; + fragment.currentPath = startingPath; + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (listener == null || currentPath == null) { + dismiss(); + } + + setStyle(STYLE_NO_TITLE, 0); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup unused, Bundle savedInstanceState) { + if (listener == null || currentPath == null) { + return null; + } + + View container = inflater.inflate(R.layout.folder_pick, null); + + statusPath = (TextView) container.findViewById(R.id.folder_status); + listView = (ListView) container.findViewById(R.id.folder_list); + okButton = (FrameLayout) container.findViewById(R.id.pick_ok); + okButtonIcon = (TextView) container.findViewById(R.id.pick_ok_icon); + + container.findViewById(R.id.pick_back).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dismiss(); + } + }); + + okButton.findViewById(R.id.pick_ok).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + listener.folderPicked(currentPath); + dismiss(); + } + }); + + if (!ThemeHelper.getInstance().getTheme().isLightTheme) { + ((TextView) container.findViewById(R.id.pick_back_icon)).setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_action_cancel_dark, 0, 0, 0); + ((TextView) container.findViewById(R.id.pick_ok_icon)).setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_action_done_dark, 0, 0, 0); + } + + adapter = new ArrayAdapter(inflater.getContext(), 0) { + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = LayoutInflater.from(getContext()).inflate(android.R.layout.simple_list_item_1, null); + } + TextView text = (TextView) convertView; + + String name = getItem(position); + + text.setText(name); + + text.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (hasParent) { + if (position == 0) { + File parent = currentPath.getParentFile(); + moveTo(parent); + } else if (position > 0 && position <= directories.size()) { + File dir = directories.get(position - 1); + moveTo(dir); + } + } else { + if (position >= 0 && position < directories.size()) { + File dir = directories.get(position); + moveTo(dir); + } + } + } + }); + + return text; + } + }; + + listView.setAdapter(adapter); + + moveTo(currentPath); + + return container; + } + + private boolean validPath(File path) { + return path != null && path.isDirectory() && path.canRead() && path.canWrite(); + } + + private void moveTo(File path) { + if (path != null && path.isDirectory()) { + File[] listFiles = path.listFiles(); + if (listFiles != null) { + currentPath = path; + statusPath.setText(currentPath.getAbsolutePath()); + List dirs = new ArrayList<>(); + for (File file : path.listFiles()) { + if (file.isDirectory()) { + dirs.add(file); + } + } + + setDirs(dirs); + } + } + + validState(); + } + + private void validState() { + if (validPath(currentPath)) { + okButton.setEnabled(true); + okButtonIcon.setEnabled(true); + } else { + okButton.setEnabled(false); + okButtonIcon.setEnabled(false); + } + } + + private void setDirs(List dirs) { + directories = dirs; + adapter.clear(); + + if (currentPath.getParent() != null) { + adapter.add(".."); + hasParent = true; + } else { + hasParent = false; + } + for (File file : dirs) { + adapter.add(file.getName()); + } + adapter.notifyDataSetChanged(); + } + + public static interface FolderPickListener { + public void folderPicked(File path); + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/fragment/ImageViewFragment.java b/Clover/app/src/main/java/org/floens/chan/ui/fragment/ImageViewFragment.java index 0e4a0017..ec29c22e 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/fragment/ImageViewFragment.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/fragment/ImageViewFragment.java @@ -222,9 +222,9 @@ public class ImageViewFragment extends Fragment implements ThumbnailImageViewCal } else if (item.getItemId() == R.id.action_open_browser) { Utils.openLink(context, post.imageUrl); } else if (item.getItemId() == R.id.action_image_save) { - ImageSaver.save(context, post.imageUrl, post.filename, post.ext, false); + ImageSaver.saveImage(context, post.imageUrl, ChanPreferences.getImageSaveOriginalFilename() ? post.tim : post.filename, post.ext, false); } else if (item.getItemId() == R.id.action_share) { - ImageSaver.save(context, post.imageUrl, post.filename, post.ext, true); + ImageSaver.saveImage(context, post.imageUrl, ChanPreferences.getImageSaveOriginalFilename() ? post.tim : post.filename, post.ext, true); } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/fragment/SettingsFragment.java b/Clover/app/src/main/java/org/floens/chan/ui/fragment/SettingsFragment.java index b4bd3451..b7129892 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/fragment/SettingsFragment.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/fragment/SettingsFragment.java @@ -32,7 +32,6 @@ import android.widget.Toast; import org.floens.chan.R; import org.floens.chan.core.ChanPreferences; import org.floens.chan.ui.activity.AboutActivity; -import org.floens.chan.ui.activity.BaseActivity; import org.floens.chan.ui.activity.SettingsActivity; import org.floens.chan.utils.ThemeHelper; diff --git a/Clover/app/src/main/java/org/floens/chan/utils/ImageSaver.java b/Clover/app/src/main/java/org/floens/chan/utils/ImageSaver.java index 03c55d72..7ee99da7 100644 --- a/Clover/app/src/main/java/org/floens/chan/utils/ImageSaver.java +++ b/Clover/app/src/main/java/org/floens/chan/utils/ImageSaver.java @@ -38,45 +38,57 @@ import org.floens.chan.core.net.ByteArrayRequest; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.util.ArrayList; import java.util.List; public class ImageSaver { private static final String TAG = "ImageSaver"; - public static void saveAll(Context context, String folderName, final List list) { + public static void saveAll(final Context context, String folderName, final List list) { final DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); - String folderPath = ChanPreferences.getImageSaveDirectory() + File.separator + folderName; - File folder = Environment.getExternalStoragePublicDirectory(folderPath); - int nextFileNameNumber = 0; - while (folder.exists() || folder.isFile()) { - folderPath = ChanPreferences.getImageSaveDirectory() + File.separator + folderName + "_" - + Integer.toString(nextFileNameNumber++); - folder = Environment.getExternalStoragePublicDirectory(folderPath); - } + folderName = filterName(folderName); + + final File subFolder = findUnused(new File(ChanPreferences.getImageSaveDirectory() + File.separator + folderName), true); + + String text = context.getString(R.string.download_confirm) + .replace("COUNT", Integer.toString(list.size())) + .replace("FOLDER", subFolder.getAbsolutePath()); - final String finalFolderPath = folderPath; - String text = context.getString(R.string.download_confirm).replace("COUNT", Integer.toString(list.size())) - .replace("FOLDER", folderPath); - final File finalFolder = folder; new AlertDialog.Builder(context).setMessage(text).setNegativeButton(R.string.cancel, null) .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { + if (!subFolder.isDirectory() && !subFolder.mkdirs()) { + Toast.makeText(context, R.string.image_save_directory_error, Toast.LENGTH_LONG).show(); + return; + } + + final List files = new ArrayList<>(list.size()); + for (DownloadPair uri : list) { + String name = filterName(uri.imageName); + // Finding unused filenames won't actually work, because the file doesn't get + // saved right away. The download manager will also prevent if there's a name collision. + File destination = findUnused(new File(subFolder, name), false); + + Pair p = new Pair(); + p.uri = Uri.parse(uri.imageUrl); + p.file = destination; + files.add(p); + } + new Thread(new Runnable() { @Override public void run() { - finalFolder.mkdirs(); - - for (Uri uri : list) { + for (Pair pair : files) { DownloadManager.Request request; try { - request = new DownloadManager.Request(uri); + request = new DownloadManager.Request(pair.uri); } catch (IllegalArgumentException e) { continue; } - request.setDestinationInExternalPublicDir(finalFolderPath, uri.getLastPathSegment()); + request.setDestinationUri(Uri.fromFile(pair.file)); request.setVisibleInDownloadsUi(false); request.allowScanningByMediaScanner(); @@ -88,26 +100,23 @@ public class ImageSaver { }).show(); } - public static void save(final Context context, String imageUrl, final String name, final String extension, - final boolean share) { + public static void saveImage(final Context context, String imageUrl, String name, final String extension, final boolean share) { + File saveDir = ChanPreferences.getImageSaveDirectory(); + + if (!saveDir.isDirectory() && !saveDir.mkdirs()) { + Toast.makeText(context, R.string.image_save_directory_error, Toast.LENGTH_LONG).show(); + return; + } + + String fileName = filterName(name + "." + extension); + final File file = findUnused(new File(saveDir, fileName), false); + + Logger.test(file.getAbsolutePath()); + ChanApplication.getVolleyRequestQueue().add(new ByteArrayRequest(imageUrl, new Response.Listener() { @Override public void onResponse(byte[] data) { - storeImage(context, data, name, extension, new SaveCallback() { - @Override - public void onUri(String name, Uri uri) { - if (!share) { - String message = context.getResources().getString(R.string.image_save_succeeded) + " " - + name; - Toast.makeText(context, message, Toast.LENGTH_LONG).show(); - } else { - Intent intent = new Intent(Intent.ACTION_SEND); - intent.setType("image/*"); - intent.putExtra(Intent.EXTRA_STREAM, uri); - context.startActivity(Intent.createChooser(intent, context.getString(R.string.action_share))); - } - } - }); + storeImage(context, data, file, share); } }, new Response.ErrorListener() { @Override @@ -117,71 +126,81 @@ public class ImageSaver { })); } - private static void storeImage(final Context context, byte[] data, String name, String extension, - final SaveCallback callback) { - String errorReason = null; - - try { - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - errorReason = context.getString(R.string.image_save_not_mounted); - throw new IOException(errorReason); - } + private static String filterName(String name) { + return name.replaceAll("[^\\w.]", ""); + } - File path = new File(Environment.getExternalStorageDirectory() + File.separator - + ChanPreferences.getImageSaveDirectory()); + private static File findUnused(File start, boolean isDir) { + String base; + String extension; - if (!path.exists()) { - if (!path.mkdirs()) { - errorReason = context.getString(R.string.image_save_directory_error); - throw new IOException(errorReason); - } + if (isDir) { + base = start.getAbsolutePath(); + extension = null; + } else { + String[] splitted = start.getAbsolutePath().split("\\.(?=[^\\.]+$)"); + if (splitted.length == 2) { + base = splitted[0]; + extension = "." + splitted[1]; + } else { + base = splitted[0]; + extension = "."; } + } - final File savedFile = saveFile(path, data, name, extension); - if (savedFile == null) { - errorReason = context.getString(R.string.image_save_failed); - throw new IOException(errorReason); + File test; + if (isDir) { + test = new File(base); + } else { + test = new File(base + extension); + } + int index = 0; + int tries = 0; + while (test.exists() && tries++ < 100) { + if (isDir) { + test = new File(base + "_" + index); + } else { + test = new File(base + "_" + index + extension); } + index++; + } - MediaScannerConnection.scanFile(context, new String[]{savedFile.toString()}, null, - new MediaScannerConnection.OnScanCompletedListener() { - @Override - public void onScanCompleted(String path, final Uri uri) { - Utils.runOnUiThread(new Runnable() { - @Override - public void run() { - Logger.i(TAG, "Media scan succeeded: " + uri); - callback.onUri(savedFile.toString(), uri); - } - }); - } - } - ); + return test; + } - } catch (IOException e) { - e.printStackTrace(); + private static void storeImage(final Context context, final byte[] data, final File destination, final boolean share) { + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + Toast.makeText(context, R.string.image_save_not_mounted, Toast.LENGTH_LONG).show(); + return; + } - if (errorReason == null) - errorReason = context.getString(R.string.image_save_failed); + new Thread(new Runnable() { + @Override + public void run() { + final boolean res = saveByteArray(destination, data); - Toast.makeText(context, errorReason, Toast.LENGTH_LONG).show(); - } + Utils.runOnUiThread(new Runnable() { + @Override + public void run() { + if (!res) { + Toast.makeText(context, R.string.image_save_failed, Toast.LENGTH_LONG).show(); + } else { + scanFile(context, destination.getAbsolutePath(), share); + } + } + }); + } + }).start(); } - private static File saveFile(File path, byte[] source, String name, String extension) { - File destination = new File(path, name + "." + extension); - int nextFileNameNumber = 0; - String newName; - while (destination.exists()) { - newName = name + "_" + Integer.toString(nextFileNameNumber++) + "." + extension; - destination = new File(path, newName); - } - + private static boolean saveByteArray(File destination, byte[] source) { + boolean res = false; FileOutputStream outputStream = null; try { outputStream = new FileOutputStream(destination); outputStream.write(source); outputStream.close(); + res = true; } catch (IOException e) { e.printStackTrace(); } finally { @@ -192,13 +211,49 @@ public class ImageSaver { } } } + return res; + } + + private static void scanFile(final Context context, final String path, final boolean shareAfterwards) { + Logger.test("Scan: " + path); + + MediaScannerConnection.scanFile(context, new String[]{path}, null, + new MediaScannerConnection.OnScanCompletedListener() { + @Override + public void onScanCompleted(String unused, final Uri uri) { + Utils.runOnUiThread(new Runnable() { + @Override + public void run() { + Logger.i(TAG, "File saved & media scan succeeded: " + uri); - Logger.i(TAG, "Saved image to: " + destination.getPath()); + if (shareAfterwards) { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType("image/*"); + intent.putExtra(Intent.EXTRA_STREAM, uri); + context.startActivity(Intent.createChooser(intent, context.getString(R.string.action_share))); + } else { + String message = context.getResources().getString(R.string.image_save_succeeded) + " " + path; + Toast.makeText(context, message, Toast.LENGTH_LONG).show(); + } + } + }); + } + } + ); + } + + public static class DownloadPair { + public String imageUrl; + public String imageName; - return destination; + public DownloadPair(String uri, String name) { + this.imageUrl = uri; + this.imageName = name; + } } - private static interface SaveCallback { - public void onUri(String name, Uri uri); + private static class Pair { + public Uri uri; + public File file; } } diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_action_cancel.png b/Clover/app/src/main/res/drawable-hdpi/ic_action_cancel.png new file mode 100644 index 00000000..f889617e Binary files /dev/null and b/Clover/app/src/main/res/drawable-hdpi/ic_action_cancel.png differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_action_cancel_dark.png b/Clover/app/src/main/res/drawable-hdpi/ic_action_cancel_dark.png new file mode 100644 index 00000000..e206f296 Binary files /dev/null and b/Clover/app/src/main/res/drawable-hdpi/ic_action_cancel_dark.png differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_action_cancel.png b/Clover/app/src/main/res/drawable-mdpi/ic_action_cancel.png new file mode 100644 index 00000000..e84853e4 Binary files /dev/null and b/Clover/app/src/main/res/drawable-mdpi/ic_action_cancel.png differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_action_cancel_dark.png b/Clover/app/src/main/res/drawable-mdpi/ic_action_cancel_dark.png new file mode 100644 index 00000000..70e6d2d2 Binary files /dev/null and b/Clover/app/src/main/res/drawable-mdpi/ic_action_cancel_dark.png differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_action_cancel.png b/Clover/app/src/main/res/drawable-xhdpi/ic_action_cancel.png new file mode 100644 index 00000000..58e2e3b4 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xhdpi/ic_action_cancel.png differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_action_cancel_dark.png b/Clover/app/src/main/res/drawable-xhdpi/ic_action_cancel_dark.png new file mode 100644 index 00000000..d1634205 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xhdpi/ic_action_cancel_dark.png differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_cancel.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_action_cancel.png new file mode 100644 index 00000000..a9bbcde5 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxhdpi/ic_action_cancel.png differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_cancel_dark.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_action_cancel_dark.png new file mode 100644 index 00000000..5dc21435 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxhdpi/ic_action_cancel_dark.png differ diff --git a/Clover/app/src/main/res/layout/folder_pick.xml b/Clover/app/src/main/res/layout/folder_pick.xml new file mode 100644 index 00000000..4a8f2f1d --- /dev/null +++ b/Clover/app/src/main/res/layout/folder_pick.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Clover/app/src/main/res/layout/post_replies.xml b/Clover/app/src/main/res/layout/post_replies.xml index 6f7bca10..45e7c3fd 100644 --- a/Clover/app/src/main/res/layout/post_replies.xml +++ b/Clover/app/src/main/res/layout/post_replies.xml @@ -50,7 +50,7 @@ along with this program. If not, see . android:drawableLeft="@drawable/ic_action_back" android:drawablePadding="8dp" android:gravity="center_vertical" - android:text="@string/post_replies_back"/> + android:text="@string/back"/> . android:drawableLeft="@drawable/ic_action_done" android:drawablePadding="8dp" android:gravity="center_vertical" - android:text="@string/post_replies_close"/> + android:text="@string/close"/> diff --git a/Clover/app/src/main/res/values/strings.xml b/Clover/app/src/main/res/values/strings.xml index 1a005065..13e236d5 100644 --- a/Clover/app/src/main/res/values/strings.xml +++ b/Clover/app/src/main/res/values/strings.xml @@ -81,8 +81,6 @@ along with this program. If not, see . replies image images - Back - Close Info Quick reply @@ -123,6 +121,8 @@ along with this program. If not, see . Error deleting post Only delete the image + Choose + Edit boards Add or remove boards Thread watcher @@ -153,6 +153,8 @@ along with this program. If not, see . pages + Image save folder + Save original filename Ask before opening links Start playing videos immediately Auto refresh threads diff --git a/Clover/app/src/main/res/xml/preference.xml b/Clover/app/src/main/res/xml/preference.xml index 32c97291..6696e4fa 100644 --- a/Clover/app/src/main/res/xml/preference.xml +++ b/Clover/app/src/main/res/xml/preference.xml @@ -1,5 +1,4 @@ - - + + + + + android:title="@string/preference_force_phone_layout" /> + android:title="@string/preference_anonymize" /> + android:title="@string/preference_anonymize_ids" />