mirror of
https://github.com/gsantner/dandelion
synced 2024-11-21 20:02:07 +01:00
Rework screenshot saving and sharing; add new share options:
* Share option: Launcher shortcut (fixes #170) * Share option: Copy link of current page to clipboard * Share otpion: Export as PDF / print
This commit is contained in:
parent
d53128e5cb
commit
51093e0c3d
35 changed files with 1222 additions and 138 deletions
|
@ -7,6 +7,8 @@
|
||||||
<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.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.INSTALL_SHORTCUT" />
|
||||||
|
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name="com.github.dfa.diaspora_android.App"
|
android:name="com.github.dfa.diaspora_android.App"
|
||||||
|
|
|
@ -25,6 +25,7 @@ import android.app.Activity;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
@ -39,8 +40,10 @@ import android.webkit.JavascriptInterface;
|
||||||
import android.webkit.ValueCallback;
|
import android.webkit.ValueCallback;
|
||||||
import android.webkit.WebChromeClient;
|
import android.webkit.WebChromeClient;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.github.dfa.diaspora_android.App;
|
import com.github.dfa.diaspora_android.App;
|
||||||
|
import com.github.dfa.diaspora_android.BuildConfig;
|
||||||
import com.github.dfa.diaspora_android.R;
|
import com.github.dfa.diaspora_android.R;
|
||||||
import com.github.dfa.diaspora_android.data.DiasporaUserProfile;
|
import com.github.dfa.diaspora_android.data.DiasporaUserProfile;
|
||||||
import com.github.dfa.diaspora_android.ui.theme.ThemedAlertDialogBuilder;
|
import com.github.dfa.diaspora_android.ui.theme.ThemedAlertDialogBuilder;
|
||||||
|
@ -53,10 +56,14 @@ import com.github.dfa.diaspora_android.web.DiasporaStreamWebChromeClient;
|
||||||
import com.github.dfa.diaspora_android.web.FileUploadWebChromeClient;
|
import com.github.dfa.diaspora_android.web.FileUploadWebChromeClient;
|
||||||
import com.github.dfa.diaspora_android.web.WebHelper;
|
import com.github.dfa.diaspora_android.web.WebHelper;
|
||||||
|
|
||||||
|
import net.gsantner.opoc.util.PermissionChecker;
|
||||||
|
import net.gsantner.opoc.util.ShareUtil;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fragment that displays the Stream of the diaspora* user
|
* Fragment that displays the Stream of the diaspora* user
|
||||||
|
@ -97,6 +104,9 @@ public class DiasporaStreamFragment extends BrowserFragment {
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
inflater.inflate(R.menu.stream__menu_top, menu);
|
inflater.inflate(R.menu.stream__menu_top, menu);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
|
menu.findItem(R.id.action_share_pdf).setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
final boolean darkBg = ContextUtils.get().shouldColorOnTopBeLight(AppSettings.get().getPrimaryColor());
|
final boolean darkBg = ContextUtils.get().shouldColorOnTopBeLight(AppSettings.get().getPrimaryColor());
|
||||||
ContextUtils.get().tintMenuItems(menu, true, ContextCompat.getColor(getActivity(), darkBg ? R.color.white : R.color.black));
|
ContextUtils.get().tintMenuItems(menu, true, ContextCompat.getColor(getActivity(), darkBg ? R.color.white : R.color.black));
|
||||||
|
@ -118,6 +128,8 @@ public class DiasporaStreamFragment extends BrowserFragment {
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
AppLog.d(this, "StreamFragment.onOptionsItemSelected()");
|
AppLog.d(this, "StreamFragment.onOptionsItemSelected()");
|
||||||
|
ShareUtil shu = new ShareUtil(getContext()).setFileProviderAuthority(BuildConfig.APPLICATION_ID);
|
||||||
|
PermissionChecker permc = new PermissionChecker(getActivity());
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.action_reload: {
|
case R.id.action_reload: {
|
||||||
if (WebHelper.isOnline(getContext())) {
|
if (WebHelper.isOnline(getContext())) {
|
||||||
|
@ -144,13 +156,47 @@ public class DiasporaStreamFragment extends BrowserFragment {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case R.id.action_share_pdf: {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
|
shu.createPdf(webView, "dandelion-" + ShareUtil.SDF_SHORT.format(new Date()));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case R.id.action_share_link_to_clipboard: {
|
||||||
|
shu.setClipboard(webView.getUrl());
|
||||||
|
Toast.makeText(getContext(), R.string.share__toast_link_address_copied, Toast.LENGTH_SHORT).show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case R.id.action_create_launcher_shortcut: {
|
||||||
|
if (webView.getUrl() != null) {
|
||||||
|
Intent intent = new Intent(getContext(), MainActivity.class);
|
||||||
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
|
intent.setData(Uri.parse(webView.getUrl()));
|
||||||
|
shu.createLauncherDesktopShortcut(intent, R.drawable.ic_launcher, webView.getTitle());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
case R.id.action_take_screenshot: {
|
case R.id.action_take_screenshot: {
|
||||||
makeScreenshotOfWebView(false);
|
if (permc.doIfExtStoragePermissionGranted(getString(R.string.permissions_screenshot))) {
|
||||||
|
File fileSaveDirectory = appSettings.getAppSaveDirectory();
|
||||||
|
if (permc.mkdirIfStoragePermissionGranted(fileSaveDirectory)) {
|
||||||
|
Bitmap bmp = ShareUtil.getBitmapFromWebView(webView);
|
||||||
|
String filename = "dandelion-" + ShareUtil.SDF_SHORT.format(new Date()) + ".jpg";
|
||||||
|
_cu.writeImageToFileJpeg(new File(fileSaveDirectory, filename), bmp);
|
||||||
|
Snackbar.make(webView, getString(R.string.share__toast_screenshot)
|
||||||
|
+ " " + filename, Snackbar.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case R.id.action_share_screenshot: {
|
case R.id.action_share_screenshot: {
|
||||||
makeScreenshotOfWebView(true);
|
if (permc.doIfExtStoragePermissionGranted(getString(R.string.permissions_screenshot))) {
|
||||||
|
shu.shareImage(ShareUtil.getBitmapFromWebView(webView), Bitmap.CompressFormat.JPEG);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -315,8 +315,8 @@ public class DiasporaPodList implements Iterable<DiasporaPodList.DiasporaPod>, S
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Getter & Setter
|
* Getter & Setter
|
||||||
*/
|
*/
|
||||||
public List<DiasporaPodUrl> getPodUrls() {
|
public List<DiasporaPodUrl> getPodUrls() {
|
||||||
return _podUrls;
|
return _podUrls;
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,6 @@ public class ActivityUtils extends net.gsantner.opoc.util.ActivityUtils {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static Uri getFileSharingUri(Context context, File file) {
|
public static Uri getFileSharingUri(Context context, File file) {
|
||||||
|
|
||||||
return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID, file);
|
return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID, file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
|
import android.os.Environment;
|
||||||
|
|
||||||
import com.github.dfa.diaspora_android.App;
|
import com.github.dfa.diaspora_android.App;
|
||||||
import com.github.dfa.diaspora_android.BuildConfig;
|
import com.github.dfa.diaspora_android.BuildConfig;
|
||||||
|
@ -31,6 +32,7 @@ import net.gsantner.opoc.preference.SharedPreferencesPropertyBackend;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -375,6 +377,11 @@ public class AppSettings extends SharedPreferencesPropertyBackend {
|
||||||
return value != BuildConfig.VERSION_CODE && !BuildConfig.IS_TEST_BUILD;
|
return value != BuildConfig.VERSION_CODE && !BuildConfig.IS_TEST_BUILD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public File getAppSaveDirectory() {
|
||||||
|
return new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "/dandelion");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public long getLastVisitedPositionInStream() {
|
public long getLastVisitedPositionInStream() {
|
||||||
return getLong(R.string.pref_key__podprofile_last_stream_position, -1, _prefPod);
|
return getLong(R.string.pref_key__podprofile_last_stream_position, -1, _prefPod);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,18 +18,9 @@
|
||||||
*/
|
*/
|
||||||
package com.github.dfa.diaspora_android.web;
|
package com.github.dfa.diaspora_android.web;
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.MutableContextWrapper;
|
import android.content.MutableContextWrapper;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
|
||||||
import android.support.design.widget.Snackbar;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.webkit.WebSettings;
|
import android.webkit.WebSettings;
|
||||||
|
@ -38,22 +29,11 @@ import android.widget.ProgressBar;
|
||||||
|
|
||||||
import com.github.dfa.diaspora_android.App;
|
import com.github.dfa.diaspora_android.App;
|
||||||
import com.github.dfa.diaspora_android.R;
|
import com.github.dfa.diaspora_android.R;
|
||||||
import com.github.dfa.diaspora_android.activity.MainActivity;
|
|
||||||
import com.github.dfa.diaspora_android.ui.theme.ThemeHelper;
|
import com.github.dfa.diaspora_android.ui.theme.ThemeHelper;
|
||||||
import com.github.dfa.diaspora_android.ui.theme.ThemedFragment;
|
import com.github.dfa.diaspora_android.ui.theme.ThemedFragment;
|
||||||
import com.github.dfa.diaspora_android.util.ActivityUtils;
|
|
||||||
import com.github.dfa.diaspora_android.util.AppLog;
|
import com.github.dfa.diaspora_android.util.AppLog;
|
||||||
import com.github.dfa.diaspora_android.util.AppSettings;
|
import com.github.dfa.diaspora_android.util.AppSettings;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.text.DateFormat;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fragment with a webView and a ProgressBar.
|
* Fragment with a webView and a ProgressBar.
|
||||||
* This Fragment retains its instance.
|
* This Fragment retains its instance.
|
||||||
|
@ -155,96 +135,6 @@ public class BrowserFragment extends ThemedFragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
|
||||||
protected boolean makeScreenshotOfWebView(boolean hasToShareScreenshot) {
|
|
||||||
AppLog.i(this, "StreamFragment.makeScreenshotOfWebView()");
|
|
||||||
if (android.os.Build.VERSION.SDK_INT >= 23) {
|
|
||||||
int hasWRITE_EXTERNAL_STORAGE = getActivity().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
|
||||||
if (hasWRITE_EXTERNAL_STORAGE != PackageManager.PERMISSION_GRANTED) {
|
|
||||||
if (!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
|
||||||
new AlertDialog.Builder(getContext())
|
|
||||||
.setMessage(R.string.permissions_screenshot)
|
|
||||||
.setNegativeButton(android.R.string.no, null)
|
|
||||||
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
if (android.os.Build.VERSION.SDK_INT >= 23)
|
|
||||||
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
|
||||||
MainActivity.REQUEST_CODE_ASK_PERMISSIONS);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.show();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
|
||||||
MainActivity.REQUEST_CODE_ASK_PERMISSIONS);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Date dateNow = new Date();
|
|
||||||
DateFormat dateFormat = new SimpleDateFormat("yy_MM_dd--HH_mm_ss", Locale.getDefault());
|
|
||||||
File fileSaveDirectory = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "/Diaspora");
|
|
||||||
|
|
||||||
String fileSaveName = hasToShareScreenshot ? ".DfA_share.jpg" : String.format("DfA_%s.jpg", dateFormat.format(dateNow));
|
|
||||||
if (!fileSaveDirectory.exists()) {
|
|
||||||
if (!fileSaveDirectory.mkdirs()) {
|
|
||||||
AppLog.w(this, "Could not mkdir " + fileSaveDirectory.getAbsolutePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasToShareScreenshot) {
|
|
||||||
Snackbar.make(webView, getString(R.string.share__toast_screenshot) + " " + fileSaveName, Snackbar.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
Bitmap bitmap;
|
|
||||||
webView.setDrawingCacheEnabled(true);
|
|
||||||
bitmap = Bitmap.createBitmap(webView.getDrawingCache());
|
|
||||||
webView.setDrawingCacheEnabled(false);
|
|
||||||
|
|
||||||
OutputStream bitmapWriter = null;
|
|
||||||
try {
|
|
||||||
bitmapWriter = new FileOutputStream(new File(fileSaveDirectory, fileSaveName));
|
|
||||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 85, bitmapWriter);
|
|
||||||
bitmapWriter.flush();
|
|
||||||
bitmap.recycle();
|
|
||||||
} catch (Exception e) {
|
|
||||||
return false;
|
|
||||||
} finally {
|
|
||||||
if (bitmapWriter != null) {
|
|
||||||
try {
|
|
||||||
bitmapWriter.close();
|
|
||||||
} catch (IOException _ignSaveored) {/* Nothing */}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only show share intent when Action Share Screenshot was selected
|
|
||||||
if (hasToShareScreenshot) {
|
|
||||||
|
|
||||||
Uri bmpUri = ActivityUtils.getFileSharingUri(getContext(), new File(fileSaveDirectory, fileSaveName));
|
|
||||||
|
|
||||||
Intent sharingIntent = new Intent(Intent.ACTION_SEND);
|
|
||||||
sharingIntent.setType("image/jpeg");
|
|
||||||
sharingIntent.putExtra(Intent.EXTRA_SUBJECT, webView.getTitle());
|
|
||||||
sharingIntent.putExtra(Intent.EXTRA_TEXT, webView.getUrl());
|
|
||||||
sharingIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
||||||
sharingIntent.putExtra(Intent.EXTRA_STREAM, bmpUri);
|
|
||||||
|
|
||||||
PackageManager pm = getActivity().getPackageManager();
|
|
||||||
|
|
||||||
if (sharingIntent.resolveActivity(pm) != null) {
|
|
||||||
startActivity(Intent.createChooser(sharingIntent, getString(R.string.action_share_dotdotdot)));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Broadcast that this file is indexable
|
|
||||||
File file = new File(fileSaveDirectory, fileSaveName);
|
|
||||||
Uri uri = Uri.fromFile(file);
|
|
||||||
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri);
|
|
||||||
getActivity().sendBroadcast(intent);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFragmentTag() {
|
public String getFragmentTag() {
|
||||||
return TAG;
|
return TAG;
|
||||||
|
|
|
@ -230,7 +230,7 @@ public class ContextMenuWebView extends NestedWebView {
|
||||||
result.getType() == HitTestResult.SRC_ANCHOR_TYPE) {
|
result.getType() == HitTestResult.SRC_ANCHOR_TYPE) {
|
||||||
// Menu options for a hyperlink.
|
// Menu options for a hyperlink.
|
||||||
menu.setHeaderTitle(result.getExtra());
|
menu.setHeaderTitle(result.getExtra());
|
||||||
menu.add(0, ID_COPY_LINK, 0, context.getString(R.string.context_menu_copy_link)).setOnMenuItemClickListener(handler);
|
menu.add(0, ID_COPY_LINK, 0, context.getString(R.string.copy_link_to_clipboard)).setOnMenuItemClickListener(handler);
|
||||||
menu.add(0, ID_SHARE_LINK, 0, context.getString(R.string.context_menu_share_link)).setOnMenuItemClickListener(handler);
|
menu.add(0, ID_SHARE_LINK, 0, context.getString(R.string.context_menu_share_link)).setOnMenuItemClickListener(handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
34
app/src/main/java/net/gsantner/opoc/util/Callback.java
Normal file
34
app/src/main/java/net/gsantner/opoc/util/Callback.java
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*#######################################################
|
||||||
|
*
|
||||||
|
* Maintained by Gregor Santner, 2018-
|
||||||
|
* https://gsantner.net/
|
||||||
|
*
|
||||||
|
* License: Apache 2.0
|
||||||
|
* https://github.com/gsantner/opoc/#licensing
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
#########################################################*/
|
||||||
|
package net.gsantner.opoc.util;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class Callback {
|
||||||
|
public interface a1<A> {
|
||||||
|
void callback(A arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface a2<A, B> {
|
||||||
|
void callback(A arg1, B arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface a3<A, B, C> {
|
||||||
|
void callback(A arg1, B arg2, C arg3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface a4<A, B, C, D> {
|
||||||
|
void callback(A arg1, B arg2, C arg3, D arg4);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface a5<A, B, C, D, E> {
|
||||||
|
void callback(A arg1, B arg2, C arg3, D arg4, E arg5);
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,6 +31,7 @@ import android.graphics.drawable.AdaptiveIconDrawable;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.VectorDrawable;
|
import android.graphics.drawable.VectorDrawable;
|
||||||
|
import android.media.MediaScannerConnection;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.NetworkInfo;
|
import android.net.NetworkInfo;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
@ -446,6 +447,25 @@ public class ContextUtils {
|
||||||
return dp * _context.getResources().getDisplayMetrics().density;
|
return dp * _context.getResources().getDisplayMetrics().density;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request the givens paths to be scanned by MediaScanner
|
||||||
|
*
|
||||||
|
* @param files Files and folders to scan
|
||||||
|
*/
|
||||||
|
public void mediaScannerScanFile(File... files) {
|
||||||
|
if (android.os.Build.VERSION.SDK_INT > 19) {
|
||||||
|
String[] paths = new String[files.length];
|
||||||
|
for (int i = 0; i < files.length; i++) {
|
||||||
|
paths[i] = files[i].getAbsolutePath();
|
||||||
|
}
|
||||||
|
MediaScannerConnection.scanFile(_context, paths, null, null);
|
||||||
|
} else {
|
||||||
|
for (File file : files) {
|
||||||
|
_context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load an image into a {@link ImageView} and apply a color filter
|
* Load an image into a {@link ImageView} and apply a color filter
|
||||||
*/
|
*/
|
||||||
|
@ -459,7 +479,10 @@ public class ContextUtils {
|
||||||
*/
|
*/
|
||||||
public Bitmap drawableToBitmap(Drawable drawable) {
|
public Bitmap drawableToBitmap(Drawable drawable) {
|
||||||
Bitmap bitmap = null;
|
Bitmap bitmap = null;
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && (drawable instanceof VectorDrawable || drawable instanceof VectorDrawableCompat || drawable instanceof AdaptiveIconDrawable)) {
|
if (drawable instanceof VectorDrawableCompat
|
||||||
|
|| (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && drawable instanceof VectorDrawable)
|
||||||
|
|| ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && drawable instanceof AdaptiveIconDrawable))) {
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||||
drawable = (DrawableCompat.wrap(drawable)).mutate();
|
drawable = (DrawableCompat.wrap(drawable)).mutate();
|
||||||
}
|
}
|
||||||
|
|
340
app/src/main/java/net/gsantner/opoc/util/FileUtils.java
Normal file
340
app/src/main/java/net/gsantner/opoc/util/FileUtils.java
Normal file
|
@ -0,0 +1,340 @@
|
||||||
|
/*#######################################################
|
||||||
|
*
|
||||||
|
* Maintained by Gregor Santner, 2017-
|
||||||
|
* https://gsantner.net/
|
||||||
|
*
|
||||||
|
* License: Apache 2.0
|
||||||
|
* https://github.com/gsantner/opoc/#licensing
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
#########################################################*/
|
||||||
|
package net.gsantner.opoc.util;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "SpellCheckingInspection", "deprecation"})
|
||||||
|
public class FileUtils {
|
||||||
|
// Used on methods like copyFile(src, dst)
|
||||||
|
private static final int BUFFER_SIZE = 4096;
|
||||||
|
|
||||||
|
public static String readTextFile(final File file) {
|
||||||
|
try {
|
||||||
|
return readCloseTextStream(new FileInputStream(file));
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
System.err.println("readTextFile: File " + file + " not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String readCloseTextStream(final InputStream stream) {
|
||||||
|
return readCloseTextStream(stream, true).get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> readCloseTextStream(final InputStream stream, boolean concatToOneString) {
|
||||||
|
final ArrayList<String> lines = new ArrayList<>();
|
||||||
|
BufferedReader reader = null;
|
||||||
|
String line = "";
|
||||||
|
try {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
reader = new BufferedReader(new InputStreamReader(stream));
|
||||||
|
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
if (concatToOneString) {
|
||||||
|
sb.append(line).append('\n');
|
||||||
|
} else {
|
||||||
|
lines.add(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line = sb.toString();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
if (reader != null) {
|
||||||
|
try {
|
||||||
|
reader.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (concatToOneString) {
|
||||||
|
lines.clear();
|
||||||
|
lines.add(line);
|
||||||
|
}
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] readBinaryFile(final File file) {
|
||||||
|
try {
|
||||||
|
return readCloseBinaryStream(new FileInputStream(file), (int) file.length());
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
System.err.println("readBinaryFile: File " + file + " not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] readCloseBinaryStream(final InputStream stream, int byteCount) {
|
||||||
|
final ArrayList<String> lines = new ArrayList<>();
|
||||||
|
BufferedInputStream reader = null;
|
||||||
|
byte[] buf = new byte[byteCount];
|
||||||
|
int totalBytesRead = 0;
|
||||||
|
try {
|
||||||
|
reader = new BufferedInputStream(stream);
|
||||||
|
while (totalBytesRead < byteCount) {
|
||||||
|
int bytesRead = reader.read(buf, totalBytesRead, byteCount - totalBytesRead);
|
||||||
|
if (bytesRead > 0) {
|
||||||
|
totalBytesRead = totalBytesRead + bytesRead;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
if (reader != null) {
|
||||||
|
try {
|
||||||
|
reader.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read binary stream (of unknown conf size)
|
||||||
|
public static byte[] readCloseBinaryStream(final InputStream stream) {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
byte[] buffer = new byte[BUFFER_SIZE];
|
||||||
|
int read;
|
||||||
|
while ((read = stream.read(buffer)) != -1) {
|
||||||
|
baos.write(buffer, 0, read);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
if (stream != null) {
|
||||||
|
try {
|
||||||
|
stream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return baos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean writeFile(final File file, byte[] data) {
|
||||||
|
try {
|
||||||
|
OutputStream output = null;
|
||||||
|
try {
|
||||||
|
output = new BufferedOutputStream(new FileOutputStream(file));
|
||||||
|
output.write(data);
|
||||||
|
output.flush();
|
||||||
|
return true;
|
||||||
|
} finally {
|
||||||
|
if (output != null) {
|
||||||
|
output.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean writeFile(final File file, final String content) {
|
||||||
|
BufferedWriter writer = null;
|
||||||
|
try {
|
||||||
|
if (!file.getParentFile().isDirectory() && !file.getParentFile().mkdirs())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
writer = new BufferedWriter(new FileWriter(file));
|
||||||
|
writer.write(content);
|
||||||
|
writer.flush();
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
if (writer != null) {
|
||||||
|
try {
|
||||||
|
writer.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean copyFile(final File src, final File dst) {
|
||||||
|
// Just touch file if src is empty
|
||||||
|
if (src.length() == 0) {
|
||||||
|
return touch(dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream is = null;
|
||||||
|
FileOutputStream os = null;
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
is = new FileInputStream(src);
|
||||||
|
os = new FileOutputStream(dst);
|
||||||
|
byte[] buf = new byte[BUFFER_SIZE];
|
||||||
|
int len;
|
||||||
|
while ((len = is.read(buf)) > 0) {
|
||||||
|
os.write(buf, 0, len);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} finally {
|
||||||
|
if (is != null) {
|
||||||
|
is.close();
|
||||||
|
}
|
||||||
|
if (os != null) {
|
||||||
|
os.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns -1 if the file did not contain any of the needles, otherwise,
|
||||||
|
// the index of which needle was found in the contents of the file.
|
||||||
|
//
|
||||||
|
// Needless MUST be in lower-case.
|
||||||
|
public static int fileContains(File file, String... needles) {
|
||||||
|
try {
|
||||||
|
FileInputStream in = new FileInputStream(file);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
String line;
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
for (i = 0; i != needles.length; ++i)
|
||||||
|
if (line.toLowerCase(Locale.ROOT).contains(needles[i])) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
in.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean deleteRecursive(final File file) {
|
||||||
|
boolean ok = true;
|
||||||
|
if (file.exists()) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
for (File child : file.listFiles())
|
||||||
|
ok &= deleteRecursive(child);
|
||||||
|
}
|
||||||
|
ok &= file.delete();
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example: Check if this is maybe a conf: (str, "jpg", "png", "jpeg")
|
||||||
|
public static boolean hasExtension(String str, String... extensions) {
|
||||||
|
String lc = str.toLowerCase(Locale.ROOT);
|
||||||
|
for (String extension : extensions) {
|
||||||
|
if (lc.endsWith("." + extension.toLowerCase(Locale.ROOT))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean renameFile(File srcFile, File destFile) {
|
||||||
|
if (srcFile.getAbsolutePath().equals(destFile.getAbsolutePath())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// renameTo will fail in case of case-changed filename in same dir.Even on case-sensitive FS!!!
|
||||||
|
if (srcFile.getParent().equals(destFile.getParent()) && srcFile.getName().toLowerCase(Locale.getDefault()).equals(destFile.getName().toLowerCase(Locale.getDefault()))) {
|
||||||
|
File tmpFile = new File(destFile.getParent(), UUID.randomUUID().getLeastSignificantBits() + ".tmp");
|
||||||
|
if (!tmpFile.exists()) {
|
||||||
|
renameFile(srcFile, tmpFile);
|
||||||
|
srcFile = tmpFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!srcFile.renameTo(destFile)) {
|
||||||
|
if (copyFile(srcFile, destFile) && !srcFile.delete()) {
|
||||||
|
if (!destFile.delete()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
|
public static boolean renameFileInSameFolder(File srcFile, String destFilename) {
|
||||||
|
return renameFile(srcFile, new File(srcFile.getParent(), destFilename));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean touch(File file) {
|
||||||
|
try {
|
||||||
|
if (!file.exists()) {
|
||||||
|
new FileOutputStream(file).close();
|
||||||
|
}
|
||||||
|
return file.setLastModified(System.currentTimeMillis());
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get relative path to specified destination
|
||||||
|
public static String relativePath(File src, File dest) {
|
||||||
|
try {
|
||||||
|
String[] srcSplit = (src.isDirectory() ? src : src.getParentFile()).getCanonicalPath().split(Pattern.quote(File.separator));
|
||||||
|
String[] destSplit = dest.getCanonicalPath().split(Pattern.quote(File.separator));
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (; i < destSplit.length && i < srcSplit.length; ++i) {
|
||||||
|
if (!destSplit[i].equals(srcSplit[i]))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i != srcSplit.length) {
|
||||||
|
for (int iUpperDir = i; iUpperDir < srcSplit.length; ++iUpperDir) {
|
||||||
|
sb.append("..");
|
||||||
|
sb.append(File.separator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (; i < destSplit.length; ++i) {
|
||||||
|
sb.append(destSplit[i]);
|
||||||
|
sb.append(File.separator);
|
||||||
|
}
|
||||||
|
if (!dest.getPath().endsWith("/") && !dest.getPath().endsWith("\\")) {
|
||||||
|
sb.delete(sb.length() - File.separator.length(), sb.length());
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
} catch (IOException | NullPointerException exception) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
208
app/src/main/java/net/gsantner/opoc/util/NetworkUtils.java
Normal file
208
app/src/main/java/net/gsantner/opoc/util/NetworkUtils.java
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
/*#######################################################
|
||||||
|
*
|
||||||
|
* Maintained by Gregor Santner, 2017-
|
||||||
|
* https://gsantner.net/
|
||||||
|
*
|
||||||
|
* License: Apache 2.0
|
||||||
|
* https://github.com/gsantner/opoc/#licensing
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
#########################################################*/
|
||||||
|
package net.gsantner.opoc.util;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "SpellCheckingInspection", "deprecation"})
|
||||||
|
public class NetworkUtils {
|
||||||
|
private static final String UTF8 = "UTF-8";
|
||||||
|
public static final String GET = "GET";
|
||||||
|
public static final String POST = "POST";
|
||||||
|
public static final String PATCH = "PATCH";
|
||||||
|
|
||||||
|
private final static int BUFFER_SIZE = 4096;
|
||||||
|
|
||||||
|
// Downloads a file from the give url to the output file
|
||||||
|
// Creates the file's parent directory if it doesn't exist
|
||||||
|
public static boolean downloadFile(final String url, final File out) {
|
||||||
|
return downloadFile(url, out, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean downloadFile(final String url, final File out, final Callback.a1<Float> progressCallback) {
|
||||||
|
try {
|
||||||
|
return downloadFile(new URL(url), out, progressCallback);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
// Won't happen
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean downloadFile(final URL url, final File outFile, final Callback.a1<Float> progressCallback) {
|
||||||
|
InputStream input = null;
|
||||||
|
OutputStream output = null;
|
||||||
|
HttpURLConnection connection = null;
|
||||||
|
try {
|
||||||
|
connection = (HttpURLConnection) url.openConnection();
|
||||||
|
connection.connect();
|
||||||
|
input = connection.getInputStream();
|
||||||
|
|
||||||
|
if (!outFile.getParentFile().isDirectory())
|
||||||
|
if (!outFile.getParentFile().mkdirs())
|
||||||
|
return false;
|
||||||
|
output = new FileOutputStream(outFile);
|
||||||
|
|
||||||
|
int count;
|
||||||
|
int written = 0;
|
||||||
|
final float invLength = 1f / connection.getContentLength();
|
||||||
|
|
||||||
|
byte data[] = new byte[BUFFER_SIZE];
|
||||||
|
while ((count = input.read(data)) != -1) {
|
||||||
|
output.write(data, 0, count);
|
||||||
|
if (invLength != -1f && progressCallback != null) {
|
||||||
|
written += count;
|
||||||
|
progressCallback.callback(written * invLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (output != null)
|
||||||
|
output.close();
|
||||||
|
if (input != null)
|
||||||
|
input.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
if (connection != null)
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No parameters, method can be GET, POST, etc.
|
||||||
|
public static String performCall(final String url, final String method) {
|
||||||
|
try {
|
||||||
|
return performCall(new URL(url), method, "");
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String performCall(final String url, final String method, final String data) {
|
||||||
|
try {
|
||||||
|
return performCall(new URL(url), method, data);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// URL encoded parameters
|
||||||
|
public static String performCall(final String url, final String method, final HashMap<String, String> params) {
|
||||||
|
try {
|
||||||
|
return performCall(new URL(url), method, encodeQuery(params));
|
||||||
|
} catch (UnsupportedEncodingException | MalformedURLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defaults to POST
|
||||||
|
public static String performCall(final String url, final JSONObject json) {
|
||||||
|
return performCall(url, POST, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String performCall(final String url, final String method, final JSONObject json) {
|
||||||
|
try {
|
||||||
|
return performCall(new URL(url), method, json.toString());
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String performCall(final URL url, final String method, final String data) {
|
||||||
|
try {
|
||||||
|
final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||||
|
conn.setRequestMethod(method);
|
||||||
|
conn.setDoInput(true);
|
||||||
|
|
||||||
|
if (data != null && !data.isEmpty()) {
|
||||||
|
conn.setDoOutput(true);
|
||||||
|
final OutputStream output = conn.getOutputStream();
|
||||||
|
output.write(data.getBytes(Charset.forName(UTF8)));
|
||||||
|
output.flush();
|
||||||
|
output.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return FileUtils.readCloseTextStream(conn.getInputStream());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String encodeQuery(final HashMap<String, String> params) throws UnsupportedEncodingException {
|
||||||
|
final StringBuilder result = new StringBuilder();
|
||||||
|
boolean first = true;
|
||||||
|
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||||
|
if (first) first = false;
|
||||||
|
else result.append("&");
|
||||||
|
|
||||||
|
result.append(URLEncoder.encode(entry.getKey(), UTF8));
|
||||||
|
result.append("=");
|
||||||
|
result.append(URLEncoder.encode(entry.getValue(), UTF8));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HashMap<String, String> getDataMap(final String query) {
|
||||||
|
final HashMap<String, String> result = new HashMap<>();
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
String name = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < query.length(); i++) {
|
||||||
|
char c = query.charAt(i);
|
||||||
|
switch (c) {
|
||||||
|
case '=':
|
||||||
|
name = URLDecoder.decode(sb.toString(), UTF8);
|
||||||
|
sb.setLength(0);
|
||||||
|
break;
|
||||||
|
case '&':
|
||||||
|
result.put(name, URLDecoder.decode(sb.toString(), UTF8));
|
||||||
|
sb.setLength(0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sb.append(c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!name.isEmpty())
|
||||||
|
result.put(name, URLDecoder.decode(sb.toString(), UTF8));
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*#######################################################
|
||||||
|
*
|
||||||
|
* Maintained by Gregor Santner, 2017-
|
||||||
|
* https://gsantner.net/
|
||||||
|
*
|
||||||
|
* License: Apache 2.0
|
||||||
|
* https://github.com/gsantner/opoc/#licensing
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
#########################################################*/
|
||||||
|
package net.gsantner.opoc.util;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.support.v4.app.ActivityCompat;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||||
|
public class PermissionChecker {
|
||||||
|
private static final int CODE_PERMISSION_EXTERNAL_STORAGE = 4000;
|
||||||
|
|
||||||
|
private Activity _activity;
|
||||||
|
|
||||||
|
public PermissionChecker(Activity activity) {
|
||||||
|
_activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean doIfExtStoragePermissionGranted(String... optionalToastMessageForKnowingWhyNeeded) {
|
||||||
|
if (ContextCompat.checkSelfPermission(_activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
|
||||||
|
if (optionalToastMessageForKnowingWhyNeeded != null && optionalToastMessageForKnowingWhyNeeded.length > 0 && optionalToastMessageForKnowingWhyNeeded[0] != null) {
|
||||||
|
new AlertDialog.Builder(_activity)
|
||||||
|
.setMessage(optionalToastMessageForKnowingWhyNeeded[0])
|
||||||
|
.setCancelable(false)
|
||||||
|
.setNegativeButton(android.R.string.no, null)
|
||||||
|
.setPositiveButton(android.R.string.yes, (dialog, which) -> {
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= 23) {
|
||||||
|
ActivityCompat.requestPermissions(_activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, CODE_PERMISSION_EXTERNAL_STORAGE);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ActivityCompat.requestPermissions(_activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, CODE_PERMISSION_EXTERNAL_STORAGE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean checkPermissionResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||||
|
if (grantResults.length > 0) {
|
||||||
|
switch (requestCode) {
|
||||||
|
case CODE_PERMISSION_EXTERNAL_STORAGE: {
|
||||||
|
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean mkdirIfStoragePermissionGranted(File dir) {
|
||||||
|
return doIfExtStoragePermissionGranted() && (dir.exists() || dir.mkdirs());
|
||||||
|
}
|
||||||
|
}
|
453
app/src/main/java/net/gsantner/opoc/util/ShareUtil.java
Normal file
453
app/src/main/java/net/gsantner/opoc/util/ShareUtil.java
Normal file
|
@ -0,0 +1,453 @@
|
||||||
|
/*#######################################################
|
||||||
|
*
|
||||||
|
* Maintained by Gregor Santner, 2017-
|
||||||
|
* https://gsantner.net/
|
||||||
|
*
|
||||||
|
* License: Apache 2.0
|
||||||
|
* https://github.com/gsantner/opoc/#licensing
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
#########################################################*/
|
||||||
|
package net.gsantner.opoc.util;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.print.PrintAttributes;
|
||||||
|
import android.print.PrintDocumentAdapter;
|
||||||
|
import android.print.PrintJob;
|
||||||
|
import android.print.PrintManager;
|
||||||
|
import android.support.annotation.DrawableRes;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.annotation.RequiresApi;
|
||||||
|
import android.support.v4.content.FileProvider;
|
||||||
|
import android.support.v4.content.pm.ShortcutInfoCompat;
|
||||||
|
import android.support.v4.content.pm.ShortcutManagerCompat;
|
||||||
|
import android.support.v4.graphics.drawable.IconCompat;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility class to ease information sharing on Android
|
||||||
|
* Also allows to parse/fetch information out of shared information
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"UnusedReturnValue", "WeakerAccess", "SameParameterValue", "unused", "deprecation", "ConstantConditions", "ObsoleteSdkInt", "SpellCheckingInspection"})
|
||||||
|
public class ShareUtil {
|
||||||
|
public final static String EXTRA_FILEPATH = "real_file_path_2";
|
||||||
|
public final static SimpleDateFormat SDF_RFC3339_ISH = new SimpleDateFormat("yyyy-MM-dd'T'HH-mm", Locale.getDefault());
|
||||||
|
public final static SimpleDateFormat SDF_SHORT = new SimpleDateFormat("yyMMdd-HHmm", Locale.getDefault());
|
||||||
|
|
||||||
|
|
||||||
|
protected Context _context;
|
||||||
|
protected String _fileProviderAuthority;
|
||||||
|
protected String _chooserTitle;
|
||||||
|
|
||||||
|
public ShareUtil(Context context) {
|
||||||
|
_context = context;
|
||||||
|
_chooserTitle = "➥";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileProviderAuthority() {
|
||||||
|
if (TextUtils.isEmpty(_fileProviderAuthority)) {
|
||||||
|
throw new RuntimeException("Error at ShareUtil.getFileProviderAuthority(): No FileProvider authority provided");
|
||||||
|
}
|
||||||
|
return _fileProviderAuthority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShareUtil setFileProviderAuthority(String fileProviderAuthority) {
|
||||||
|
_fileProviderAuthority = fileProviderAuthority;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ShareUtil setChooserTitle(String title) {
|
||||||
|
_chooserTitle = title;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a {@link File} to an {@link Uri}
|
||||||
|
*
|
||||||
|
* @param file the file
|
||||||
|
* @return Uri for this file
|
||||||
|
*/
|
||||||
|
public Uri getUriByFileProviderAuthority(File file) {
|
||||||
|
return FileProvider.getUriForFile(_context, getFileProviderAuthority(), file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow to choose a handling app for given intent
|
||||||
|
*
|
||||||
|
* @param intent Thing to be shared
|
||||||
|
* @param chooserText The title text for the chooser, or null for default
|
||||||
|
*/
|
||||||
|
public void showChooser(Intent intent, String chooserText) {
|
||||||
|
_context.startActivity(Intent.createChooser(intent,
|
||||||
|
chooserText != null ? chooserText : _chooserTitle));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to create a new desktop shortcut on the launcher. Add permissions:
|
||||||
|
* <uses-permission android:name="android.permission.INSTALL_SHORTCUT" />
|
||||||
|
* <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
|
||||||
|
*
|
||||||
|
* @param intent The intent to be invoked on tap
|
||||||
|
* @param iconRes Icon resource for the item
|
||||||
|
* @param title Title of the item
|
||||||
|
*/
|
||||||
|
public void createLauncherDesktopShortcut(Intent intent, @DrawableRes int iconRes, String title) {
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
if (intent.getAction() == null) {
|
||||||
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShortcutInfoCompat shortcut = new ShortcutInfoCompat.Builder(_context, Long.toString(new Random().nextLong()))
|
||||||
|
.setIntent(intent)
|
||||||
|
.setIcon(IconCompat.createWithResource(_context, iconRes))
|
||||||
|
.setShortLabel(title)
|
||||||
|
.setLongLabel(title)
|
||||||
|
.build();
|
||||||
|
ShortcutManagerCompat.requestPinShortcut(_context, shortcut, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to create a new desktop shortcut on the launcher. This will not work on Api > 25. Add permissions:
|
||||||
|
* <uses-permission android:name="android.permission.INSTALL_SHORTCUT" />
|
||||||
|
* <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
|
||||||
|
*
|
||||||
|
* @param intent The intent to be invoked on tap
|
||||||
|
* @param iconRes Icon resource for the item
|
||||||
|
* @param title Title of the item
|
||||||
|
*/
|
||||||
|
public void createLauncherDesktopShortcutLegacy(Intent intent, @DrawableRes int iconRes, String title) {
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
if (intent.getAction() == null) {
|
||||||
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent creationIntent = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");
|
||||||
|
creationIntent.putExtra("duplicate", true);
|
||||||
|
creationIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent);
|
||||||
|
creationIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
|
||||||
|
creationIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(_context, iconRes));
|
||||||
|
_context.sendBroadcast(creationIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Share text with given mime-type
|
||||||
|
*
|
||||||
|
* @param text The text to share
|
||||||
|
* @param mimeType MimeType or null (uses text/plain)
|
||||||
|
*/
|
||||||
|
public void shareText(String text, @Nullable String mimeType) {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||||
|
intent.putExtra(Intent.EXTRA_TEXT, text);
|
||||||
|
intent.setType(mimeType != null ? mimeType : "text/plain");
|
||||||
|
showChooser(intent, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Share the given file as stream with given mime-type
|
||||||
|
*
|
||||||
|
* @param file The file to share
|
||||||
|
* @param mimeType The files mime type
|
||||||
|
*/
|
||||||
|
public void shareStream(File file, String mimeType) {
|
||||||
|
Uri fileUri = FileProvider.getUriForFile(_context, getFileProviderAuthority(), file);
|
||||||
|
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||||
|
intent.putExtra(Intent.EXTRA_STREAM, fileUri);
|
||||||
|
intent.putExtra(EXTRA_FILEPATH, file.getAbsolutePath());
|
||||||
|
intent.setType(mimeType);
|
||||||
|
showChooser(intent, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Share the given bitmap with given format
|
||||||
|
*
|
||||||
|
* @param bitmap Image
|
||||||
|
* @param format A {@link Bitmap.CompressFormat}, supporting JPEG,PNG,WEBP
|
||||||
|
* @return if success, true
|
||||||
|
*/
|
||||||
|
public boolean shareImage(Bitmap bitmap, Bitmap.CompressFormat format) {
|
||||||
|
return shareImage(bitmap, format, 95, "SharedImage");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Share the given bitmap with given format
|
||||||
|
*
|
||||||
|
* @param bitmap Image
|
||||||
|
* @param format A {@link Bitmap.CompressFormat}, supporting JPEG,PNG,WEBP
|
||||||
|
* @param imageName Filename without extension
|
||||||
|
* @param quality Quality of the exported image [0-100]
|
||||||
|
* @return if success, true
|
||||||
|
*/
|
||||||
|
public boolean shareImage(Bitmap bitmap, Bitmap.CompressFormat format, int quality, String imageName) {
|
||||||
|
try {
|
||||||
|
String ext = format.name().toLowerCase();
|
||||||
|
File file = File.createTempFile(imageName, "." + ext.replace("jpeg", "jpg"), _context.getExternalCacheDir());
|
||||||
|
if (bitmap != null && new ContextUtils(_context).writeImageToFile(file, bitmap, format, quality)) {
|
||||||
|
shareStream(file, "image/" + ext);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print a {@link WebView}'s contents, also allows to create a PDF
|
||||||
|
*
|
||||||
|
* @param webview WebView
|
||||||
|
* @param jobName Name of the job (affects PDF name too)
|
||||||
|
* @return {{@link PrintJob}} or null
|
||||||
|
*/
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public PrintJob print(WebView webview, String jobName) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
|
PrintDocumentAdapter printAdapter;
|
||||||
|
PrintManager printManager = (PrintManager) webview.getContext().getSystemService(Context.PRINT_SERVICE);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
printAdapter = webview.createPrintDocumentAdapter(jobName);
|
||||||
|
} else {
|
||||||
|
printAdapter = webview.createPrintDocumentAdapter();
|
||||||
|
}
|
||||||
|
if (printManager != null) {
|
||||||
|
return printManager.print(jobName, printAdapter, new PrintAttributes.Builder().build());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(getClass().getName(), "ERROR: Method called on too low Android API version");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See {@link #print(WebView, String) print method}
|
||||||
|
*/
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public PrintJob createPdf(WebView webview, String jobName) {
|
||||||
|
return print(webview, jobName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a picture out of {@link WebView}'s whole content
|
||||||
|
*
|
||||||
|
* @param webView The WebView to get contents from
|
||||||
|
* @return A {@link Bitmap} or null
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static Bitmap getBitmapFromWebView(WebView webView) {
|
||||||
|
try {
|
||||||
|
//Measure WebView's content
|
||||||
|
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
|
||||||
|
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
|
||||||
|
webView.measure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
webView.layout(0, 0, webView.getMeasuredWidth(), webView.getMeasuredHeight());
|
||||||
|
|
||||||
|
//Build drawing cache and store its size
|
||||||
|
webView.buildDrawingCache();
|
||||||
|
int measuredWidth = webView.getMeasuredWidth();
|
||||||
|
int measuredHeight = webView.getMeasuredHeight();
|
||||||
|
|
||||||
|
//Creates the bitmap and draw WebView's content on in
|
||||||
|
Bitmap bitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
canvas.drawBitmap(bitmap, 0, bitmap.getHeight(), new Paint());
|
||||||
|
|
||||||
|
webView.draw(canvas);
|
||||||
|
webView.destroyDrawingCache();
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
|
} catch (Exception | OutOfMemoryError e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Replace (primary) clipboard contents with given {@code text}
|
||||||
|
* @param text Text to be set
|
||||||
|
*/
|
||||||
|
public boolean setClipboard(CharSequence text) {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
|
||||||
|
android.text.ClipboardManager cm = ((android.text.ClipboardManager) _context.getSystemService(Context.CLIPBOARD_SERVICE));
|
||||||
|
if (cm != null) {
|
||||||
|
cm.setText(text);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
android.content.ClipboardManager cm = ((android.content.ClipboardManager) _context.getSystemService(Context.CLIPBOARD_SERVICE));
|
||||||
|
if (cm != null) {
|
||||||
|
ClipData clip = ClipData.newPlainText(_context.getPackageName(), text);
|
||||||
|
cm.setPrimaryClip(clip);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get clipboard contents, very failsafe and compat to older android versions
|
||||||
|
*/
|
||||||
|
public List<String> getClipboard() {
|
||||||
|
List<String> clipper = new ArrayList<>();
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
|
||||||
|
android.text.ClipboardManager cm = ((android.text.ClipboardManager) _context.getSystemService(Context.CLIPBOARD_SERVICE));
|
||||||
|
if (cm != null && !TextUtils.isEmpty(cm.getText())) {
|
||||||
|
clipper.add(cm.getText().toString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
android.content.ClipboardManager cm = ((android.content.ClipboardManager) _context.getSystemService(Context.CLIPBOARD_SERVICE));
|
||||||
|
if (cm != null && cm.hasPrimaryClip()) {
|
||||||
|
ClipData data = cm.getPrimaryClip();
|
||||||
|
for (int i = 0; data != null && i < data.getItemCount() && i < data.getItemCount(); i++) {
|
||||||
|
ClipData.Item item = data.getItemAt(i);
|
||||||
|
if (item != null && !TextUtils.isEmpty(item.getText())) {
|
||||||
|
clipper.add(data.getItemAt(i).getText().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return clipper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Share given text on a hastebin compatible server
|
||||||
|
* (https://github.com/seejohnrun/haste-server)
|
||||||
|
* Permission needed: Internet
|
||||||
|
* Pastes will be deleted after 30 days without access
|
||||||
|
*
|
||||||
|
* @param text The text to paste
|
||||||
|
* @param callback Callback after paste try
|
||||||
|
* @param serverOrNothing Supply one or no hastebin server. If empty, the default gets taken
|
||||||
|
*/
|
||||||
|
public void pasteOnHastebin(final String text, final Callback.a2<Boolean, String> callback, String... serverOrNothing) {
|
||||||
|
final Handler handler = new Handler();
|
||||||
|
final String server = (serverOrNothing != null && serverOrNothing.length > 0 && serverOrNothing[0] != null)
|
||||||
|
? serverOrNothing[0] : "https://hastebin.com";
|
||||||
|
new Thread() {
|
||||||
|
public void run() {
|
||||||
|
// Returns a simple result, handleable without json parser {"key":"feediyujiq"}
|
||||||
|
String ret = NetworkUtils.performCall(server + "/documents", NetworkUtils.POST, text);
|
||||||
|
final String key = (ret.length() > 15) ? ret.split("\"")[3] : "";
|
||||||
|
handler.post(() -> callback.callback(!key.isEmpty(), server + "/" + key));
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draft an email with given data. Unknown data can be supplied as null.
|
||||||
|
* This will open a chooser with installed mail clients where the mail can be sent from
|
||||||
|
*
|
||||||
|
* @param subject Subject (top/title) text to be prefilled in the mail
|
||||||
|
* @param body Body (content) text to be prefilled in the mail
|
||||||
|
* @param to recipients to be prefilled in the mail
|
||||||
|
*/
|
||||||
|
public void draftEmail(String subject, String body, String... to) {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_SENDTO);
|
||||||
|
intent.setData(Uri.parse("mailto:"));
|
||||||
|
if (subject != null) {
|
||||||
|
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
|
||||||
|
}
|
||||||
|
if (body != null) {
|
||||||
|
intent.putExtra(Intent.EXTRA_TEXT, body);
|
||||||
|
}
|
||||||
|
if (to != null && to.length > 0 && to[0] != null) {
|
||||||
|
intent.putExtra(Intent.EXTRA_EMAIL, to);
|
||||||
|
}
|
||||||
|
showChooser(intent, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to force extract a absolute filepath from an intent
|
||||||
|
*
|
||||||
|
* @param receivingIntent The intent from {@link Activity#getIntent()}
|
||||||
|
* @return A file or null if extraction did not succeed
|
||||||
|
*/
|
||||||
|
public File extractFileFromIntent(Intent receivingIntent) {
|
||||||
|
String action = receivingIntent.getAction();
|
||||||
|
String type = receivingIntent.getType();
|
||||||
|
File tmpf;
|
||||||
|
String tmps;
|
||||||
|
String fileStr;
|
||||||
|
|
||||||
|
if ((Intent.ACTION_VIEW.equals(action) || Intent.ACTION_EDIT.equals(action))) {
|
||||||
|
// Markor, S.M.T FileManager
|
||||||
|
if (receivingIntent.hasExtra((tmps = EXTRA_FILEPATH))) {
|
||||||
|
return new File(receivingIntent.getStringExtra(tmps));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Analyze data/Uri
|
||||||
|
Uri fileUri = receivingIntent.getData();
|
||||||
|
if (fileUri != null && (fileStr = fileUri.toString()) != null) {
|
||||||
|
// Uri contains file
|
||||||
|
if (fileStr.startsWith("file://")) {
|
||||||
|
return new File(fileUri.getPath());
|
||||||
|
}
|
||||||
|
if (fileStr.startsWith((tmps = "content://"))) {
|
||||||
|
fileStr = fileStr.substring(tmps.length());
|
||||||
|
String fileProvider = fileStr.substring(0, fileStr.indexOf("/"));
|
||||||
|
fileStr = fileStr.substring(fileProvider.length() + 1);
|
||||||
|
|
||||||
|
// Some file managers dont add leading slash
|
||||||
|
if (fileStr.startsWith("storage/")) {
|
||||||
|
fileStr = "/" + fileStr;
|
||||||
|
}
|
||||||
|
// Some do add some custom prefix
|
||||||
|
for (String prefix : new String[]{"file", "document", "root_files"}) {
|
||||||
|
if (fileStr.startsWith(prefix)) {
|
||||||
|
fileStr = fileStr.substring(prefix.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Next/OwnCloud Fileprovider
|
||||||
|
for (String fp : new String[]{"org.nextcloud.files", "org.nextcloud.beta.files", "org.owncloud.files"}) {
|
||||||
|
if (fileProvider.equals(fp) && fileStr.startsWith(tmps = "external_files/")) {
|
||||||
|
return new File(Uri.decode("/storage/" + fileStr.substring(tmps.length())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// AOSP File Manager/Documents
|
||||||
|
if (fileProvider.equals("com.android.externalstorage.documents") && fileStr.startsWith(tmps = "/primary%3A")) {
|
||||||
|
return new File(Uri.decode(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + fileStr.substring(tmps.length())));
|
||||||
|
}
|
||||||
|
// Mi File Explorer
|
||||||
|
if (fileProvider.equals("com.mi.android.globalFileexplorer.myprovider") && fileStr.startsWith(tmps = "external_files")) {
|
||||||
|
return new File(Uri.decode(Environment.getExternalStorageDirectory().getAbsolutePath() + fileStr.substring(tmps.length())));
|
||||||
|
}
|
||||||
|
// URI Encoded paths with full path after content://package/
|
||||||
|
if (fileStr.startsWith("/") || fileStr.startsWith("%2F")) {
|
||||||
|
tmpf = new File(Uri.decode(fileStr));
|
||||||
|
if (tmpf.exists()) {
|
||||||
|
return tmpf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,9 +14,19 @@
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_take_screenshot"
|
android:id="@+id/action_take_screenshot"
|
||||||
android:title="@string/share__take_screenshot" />
|
android:title="@string/share__take_screenshot" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_share_pdf"
|
||||||
|
android:title="@string/pdf"
|
||||||
|
android:visible="false" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_share_link"
|
android:id="@+id/action_share_link"
|
||||||
android:title="@string/share__share_link_as_text" />
|
android:title="@string/share__share_link_as_text" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_share_link_to_clipboard"
|
||||||
|
android:title="@string/copy_link_to_clipboard" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_create_launcher_shortcut"
|
||||||
|
android:title="@string/launcher_shortcut" />
|
||||||
</menu>
|
</menu>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
<!-- Drawer, Menu, Toolbar, ContextMenu -->
|
<!-- Drawer, Menu, Toolbar, ContextMenu -->
|
||||||
<string name="context_menu_share_image">Del billede</string>
|
<string name="context_menu_share_image">Del billede</string>
|
||||||
<string name="context_menu_open_external_browser">Åben i ekstern browser…</string>
|
<string name="context_menu_open_external_browser">Åben i ekstern browser…</string>
|
||||||
<string name="context_menu_copy_link">Kopier link-adresse til udklipsholder</string>
|
<string name="copy_link_to_clipboard">Kopier link-adresse til udklipsholder</string>
|
||||||
<!-- More from MainActivity -->
|
<!-- More from MainActivity -->
|
||||||
<!-- Permissions -->
|
<!-- Permissions -->
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
<string name="context_menu_save_image">Bild speichern</string>
|
<string name="context_menu_save_image">Bild speichern</string>
|
||||||
<string name="context_menu_share_image">Bild teilen</string>
|
<string name="context_menu_share_image">Bild teilen</string>
|
||||||
<string name="context_menu_open_external_browser">In externem Browser öffnen…</string>
|
<string name="context_menu_open_external_browser">In externem Browser öffnen…</string>
|
||||||
<string name="context_menu_copy_link">Link-Adresse kopieren</string>
|
<string name="copy_link_to_clipboard">Link-Adresse kopieren</string>
|
||||||
<string name="context_menu_copy_image_link">Bild-Adresse kopieren</string>
|
<string name="context_menu_copy_image_link">Bild-Adresse kopieren</string>
|
||||||
<!-- More from MainActivity -->
|
<!-- More from MainActivity -->
|
||||||
<string name="unable_to_load_image">Konnte Bild nicht laden…</string>
|
<string name="unable_to_load_image">Konnte Bild nicht laden…</string>
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
<string name="context_menu_save_image">Guardar imagen</string>
|
<string name="context_menu_save_image">Guardar imagen</string>
|
||||||
<string name="context_menu_share_image">Compartir imagen</string>
|
<string name="context_menu_share_image">Compartir imagen</string>
|
||||||
<string name="context_menu_open_external_browser">Abrir en navegador externo…</string>
|
<string name="context_menu_open_external_browser">Abrir en navegador externo…</string>
|
||||||
<string name="context_menu_copy_link">Copiar dirección del enlace al portapapeles</string>
|
<string name="copy_link_to_clipboard">Copiar dirección del enlace al portapapeles</string>
|
||||||
<string name="context_menu_copy_image_link">Copiar dirección de imagen al portapapeles</string>
|
<string name="context_menu_copy_image_link">Copiar dirección de imagen al portapapeles</string>
|
||||||
<!-- More from MainActivity -->
|
<!-- More from MainActivity -->
|
||||||
<string name="unable_to_load_image">No se pudo cargar la imagen</string>
|
<string name="unable_to_load_image">No se pudo cargar la imagen</string>
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
<string name="context_menu_save_image">Enregistrer l\'image</string>
|
<string name="context_menu_save_image">Enregistrer l\'image</string>
|
||||||
<string name="context_menu_share_image">Partager l\'image</string>
|
<string name="context_menu_share_image">Partager l\'image</string>
|
||||||
<string name="context_menu_open_external_browser">Ouvrir dans un navigateur externe…</string>
|
<string name="context_menu_open_external_browser">Ouvrir dans un navigateur externe…</string>
|
||||||
<string name="context_menu_copy_link">Copier le lien dans le presse-papier</string>
|
<string name="copy_link_to_clipboard">Copier le lien dans le presse-papier</string>
|
||||||
<string name="context_menu_copy_image_link">Copier le lien de l\'image dans le presse-papiers</string>
|
<string name="context_menu_copy_image_link">Copier le lien de l\'image dans le presse-papiers</string>
|
||||||
<!-- More from MainActivity -->
|
<!-- More from MainActivity -->
|
||||||
<string name="unable_to_load_image">Impossible de récupérer l\'image</string>
|
<string name="unable_to_load_image">Impossible de récupérer l\'image</string>
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
<string name="context_menu_save_image">Gardar imaxe</string>
|
<string name="context_menu_save_image">Gardar imaxe</string>
|
||||||
<string name="context_menu_share_image">Compartir imaxe</string>
|
<string name="context_menu_share_image">Compartir imaxe</string>
|
||||||
<string name="context_menu_open_external_browser">Abrir nun navegador externo…</string>
|
<string name="context_menu_open_external_browser">Abrir nun navegador externo…</string>
|
||||||
<string name="context_menu_copy_link">Copiar ligazón ao portapapeis</string>
|
<string name="copy_link_to_clipboard">Copiar ligazón ao portapapeis</string>
|
||||||
<string name="context_menu_copy_image_link">Copia enderezo da imaxe ao portapapeis</string>
|
<string name="context_menu_copy_image_link">Copia enderezo da imaxe ao portapapeis</string>
|
||||||
<!-- More from MainActivity -->
|
<!-- More from MainActivity -->
|
||||||
<string name="unable_to_load_image">Non se cargou a imaxe</string>
|
<string name="unable_to_load_image">Non se cargou a imaxe</string>
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
<string name="context_menu_save_image">Kép mentése</string>
|
<string name="context_menu_save_image">Kép mentése</string>
|
||||||
<string name="context_menu_share_image">Kép megosztása</string>
|
<string name="context_menu_share_image">Kép megosztása</string>
|
||||||
<string name="context_menu_open_external_browser">Megnyitás külső böngészőben…</string>
|
<string name="context_menu_open_external_browser">Megnyitás külső böngészőben…</string>
|
||||||
<string name="context_menu_copy_link">Link címének másolása a vágólapra</string>
|
<string name="copy_link_to_clipboard">Link címének másolása a vágólapra</string>
|
||||||
<string name="context_menu_copy_image_link">Kép címének másolása a vágólapra</string>
|
<string name="context_menu_copy_image_link">Kép címének másolása a vágólapra</string>
|
||||||
<!-- More from MainActivity -->
|
<!-- More from MainActivity -->
|
||||||
<string name="unable_to_load_image">Nem lehet betölteni a képet</string>
|
<string name="unable_to_load_image">Nem lehet betölteni a képet</string>
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
<string name="context_menu_save_image">Salva immagine</string>
|
<string name="context_menu_save_image">Salva immagine</string>
|
||||||
<string name="context_menu_share_image">Condividi immagine</string>
|
<string name="context_menu_share_image">Condividi immagine</string>
|
||||||
<string name="context_menu_open_external_browser">Apri nel browser…</string>
|
<string name="context_menu_open_external_browser">Apri nel browser…</string>
|
||||||
<string name="context_menu_copy_link">Copia link negli appunti</string>
|
<string name="copy_link_to_clipboard">Copia link negli appunti</string>
|
||||||
<string name="context_menu_copy_image_link">Copia indirizzo dell\'immagine negli appunti</string>
|
<string name="context_menu_copy_image_link">Copia indirizzo dell\'immagine negli appunti</string>
|
||||||
<!-- More from MainActivity -->
|
<!-- More from MainActivity -->
|
||||||
<string name="unable_to_load_image">Impossibile caricare immagine</string>
|
<string name="unable_to_load_image">Impossibile caricare immagine</string>
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
<string name="context_menu_save_image">画像を保存</string>
|
<string name="context_menu_save_image">画像を保存</string>
|
||||||
<string name="context_menu_share_image">画像をシェア</string>
|
<string name="context_menu_share_image">画像をシェア</string>
|
||||||
<string name="context_menu_open_external_browser">外部ブラウザーで開く…</string>
|
<string name="context_menu_open_external_browser">外部ブラウザーで開く…</string>
|
||||||
<string name="context_menu_copy_link">リンクアドレスをクリップボードへコピー</string>
|
<string name="copy_link_to_clipboard">リンクアドレスをクリップボードへコピー</string>
|
||||||
<string name="context_menu_copy_image_link">画像のアドレスをクリップボードへコピー</string>
|
<string name="context_menu_copy_image_link">画像のアドレスをクリップボードへコピー</string>
|
||||||
<!-- More from MainActivity -->
|
<!-- More from MainActivity -->
|
||||||
<string name="unable_to_load_image">画像を読み込むことができません</string>
|
<string name="unable_to_load_image">画像を読み込むことができません</string>
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
<string name="context_menu_save_image">Sekles tugna</string>
|
<string name="context_menu_save_image">Sekles tugna</string>
|
||||||
<string name="context_menu_share_image">Bḍu tugna</string>
|
<string name="context_menu_share_image">Bḍu tugna</string>
|
||||||
<string name="context_menu_open_external_browser">Ldi deg iminig azɣaray…</string>
|
<string name="context_menu_open_external_browser">Ldi deg iminig azɣaray…</string>
|
||||||
<string name="context_menu_copy_link">Nɣel aseɣwen ɣef afus</string>
|
<string name="copy_link_to_clipboard">Nɣel aseɣwen ɣef afus</string>
|
||||||
<string name="context_menu_copy_image_link">Nɣel tugna ɣef afus</string>
|
<string name="context_menu_copy_image_link">Nɣel tugna ɣef afus</string>
|
||||||
<!-- More from MainActivity -->
|
<!-- More from MainActivity -->
|
||||||
<string name="unable_to_load_image">Ur izmir ara ad d-isali tugna</string>
|
<string name="unable_to_load_image">Ur izmir ara ad d-isali tugna</string>
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
<string name="context_menu_save_image">ചിത്രം സംരക്ഷിക്കുക</string>
|
<string name="context_menu_save_image">ചിത്രം സംരക്ഷിക്കുക</string>
|
||||||
<string name="context_menu_share_image">ചിത്രം പങ്കുവയ്ക്കുക</string>
|
<string name="context_menu_share_image">ചിത്രം പങ്കുവയ്ക്കുക</string>
|
||||||
<string name="context_menu_open_external_browser">പുറമെയുള്ള ബ്രൗസറിൽ തുറക്കുക…</string>
|
<string name="context_menu_open_external_browser">പുറമെയുള്ള ബ്രൗസറിൽ തുറക്കുക…</string>
|
||||||
<string name="context_menu_copy_link">ലിങ്ക് വിലാസം ക്ലിപ്ബോർഡിലേക്ക് പകർത്തുക</string>
|
<string name="copy_link_to_clipboard">ലിങ്ക് വിലാസം ക്ലിപ്ബോർഡിലേക്ക് പകർത്തുക</string>
|
||||||
<string name="context_menu_copy_image_link">ചിത്രത്തിന്റെ വിലാസം ക്ലിപ്ബോർഡിലേക്ക് പകർത്തുക</string>
|
<string name="context_menu_copy_image_link">ചിത്രത്തിന്റെ വിലാസം ക്ലിപ്ബോർഡിലേക്ക് പകർത്തുക</string>
|
||||||
<!-- More from MainActivity -->
|
<!-- More from MainActivity -->
|
||||||
<string name="unable_to_load_image">ചിത്രം ലോഡ് ചെയ്യാൻ സാധിക്കുന്നില്ല</string>
|
<string name="unable_to_load_image">ചിത്രം ലോഡ് ചെയ്യാൻ സാധിക്കുന്നില്ല</string>
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
<string name="context_menu_save_image">Afbeelding opslaan</string>
|
<string name="context_menu_save_image">Afbeelding opslaan</string>
|
||||||
<string name="context_menu_share_image">Deel afbeelding</string>
|
<string name="context_menu_share_image">Deel afbeelding</string>
|
||||||
<string name="context_menu_open_external_browser">Geopend in externe browser…</string>
|
<string name="context_menu_open_external_browser">Geopend in externe browser…</string>
|
||||||
<string name="context_menu_copy_link">Link-adres kopiëren naar Klembord</string>
|
<string name="copy_link_to_clipboard">Link-adres kopiëren naar Klembord</string>
|
||||||
<string name="context_menu_copy_image_link">Afbeelding kopiëren naar Klembord</string>
|
<string name="context_menu_copy_image_link">Afbeelding kopiëren naar Klembord</string>
|
||||||
<!-- More from MainActivity -->
|
<!-- More from MainActivity -->
|
||||||
<string name="unable_to_load_image">Niet in staat om afbeelding te laden</string>
|
<string name="unable_to_load_image">Niet in staat om afbeelding te laden</string>
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
<string name="context_menu_save_image">Zapisz obraz</string>
|
<string name="context_menu_save_image">Zapisz obraz</string>
|
||||||
<string name="context_menu_share_image">Udostępnij obraz</string>
|
<string name="context_menu_share_image">Udostępnij obraz</string>
|
||||||
<string name="context_menu_open_external_browser">Otwórz w zewnętrznej przeglądarce…</string>
|
<string name="context_menu_open_external_browser">Otwórz w zewnętrznej przeglądarce…</string>
|
||||||
<string name="context_menu_copy_link">Skopiuj adres odnośnika do schowka</string>
|
<string name="copy_link_to_clipboard">Skopiuj adres odnośnika do schowka</string>
|
||||||
<string name="context_menu_copy_image_link">Skopiuj adres obrazu do schowka</string>
|
<string name="context_menu_copy_image_link">Skopiuj adres obrazu do schowka</string>
|
||||||
<!-- More from MainActivity -->
|
<!-- More from MainActivity -->
|
||||||
<string name="unable_to_load_image">Nie udało się wczytać obrazu</string>
|
<string name="unable_to_load_image">Nie udało się wczytać obrazu</string>
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
<string name="context_menu_save_image">Salvar imagem</string>
|
<string name="context_menu_save_image">Salvar imagem</string>
|
||||||
<string name="context_menu_share_image">Compartilhar imagem</string>
|
<string name="context_menu_share_image">Compartilhar imagem</string>
|
||||||
<string name="context_menu_open_external_browser">Abrir em navegador externo…</string>
|
<string name="context_menu_open_external_browser">Abrir em navegador externo…</string>
|
||||||
<string name="context_menu_copy_link">Copiar endereço à área de transferência</string>
|
<string name="copy_link_to_clipboard">Copiar endereço à área de transferência</string>
|
||||||
<string name="context_menu_copy_image_link">Copiar endereço de imagem à área de transferência</string>
|
<string name="context_menu_copy_image_link">Copiar endereço de imagem à área de transferência</string>
|
||||||
<!-- More from MainActivity -->
|
<!-- More from MainActivity -->
|
||||||
<string name="unable_to_load_image">Impossível carregar a imagem</string>
|
<string name="unable_to_load_image">Impossível carregar a imagem</string>
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
<string name="context_menu_save_image">Сохранить изображение</string>
|
<string name="context_menu_save_image">Сохранить изображение</string>
|
||||||
<string name="context_menu_share_image">Поделиться изображением</string>
|
<string name="context_menu_share_image">Поделиться изображением</string>
|
||||||
<string name="context_menu_open_external_browser">Открыть во внешнем браузере…</string>
|
<string name="context_menu_open_external_browser">Открыть во внешнем браузере…</string>
|
||||||
<string name="context_menu_copy_link">Скопировать адрес ссылки в буфер обмена</string>
|
<string name="copy_link_to_clipboard">Скопировать адрес ссылки в буфер обмена</string>
|
||||||
<string name="context_menu_copy_image_link">Скопировать адрес изображения в буфер обмена</string>
|
<string name="context_menu_copy_image_link">Скопировать адрес изображения в буфер обмена</string>
|
||||||
<!-- More from MainActivity -->
|
<!-- More from MainActivity -->
|
||||||
<string name="unable_to_load_image">Не удаётся загрузить изображение</string>
|
<string name="unable_to_load_image">Не удаётся загрузить изображение</string>
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
<string name="context_menu_save_image">Sarva s\'immàgine</string>
|
<string name="context_menu_save_image">Sarva s\'immàgine</string>
|
||||||
<string name="context_menu_share_image">Cumpartzi s\'immàgine</string>
|
<string name="context_menu_share_image">Cumpartzi s\'immàgine</string>
|
||||||
<string name="context_menu_open_external_browser">Aberi in un\'esploradore (browser) esternu…</string>
|
<string name="context_menu_open_external_browser">Aberi in un\'esploradore (browser) esternu…</string>
|
||||||
<string name="context_menu_copy_link">Còpia su ligàmenes in sos apuntos</string>
|
<string name="copy_link_to_clipboard">Còpia su ligàmenes in sos apuntos</string>
|
||||||
<string name="context_menu_copy_image_link">Còpia s\'indiritzu de s\'immàgine in sos apuntos</string>
|
<string name="context_menu_copy_image_link">Còpia s\'indiritzu de s\'immàgine in sos apuntos</string>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
<string name="context_menu_save_image">Spara bild</string>
|
<string name="context_menu_save_image">Spara bild</string>
|
||||||
<string name="context_menu_share_image">Dela bild</string>
|
<string name="context_menu_share_image">Dela bild</string>
|
||||||
<string name="context_menu_open_external_browser">Öppna i en extern webbläsare…</string>
|
<string name="context_menu_open_external_browser">Öppna i en extern webbläsare…</string>
|
||||||
<string name="context_menu_copy_link">Kopiera länkadress</string>
|
<string name="copy_link_to_clipboard">Kopiera länkadress</string>
|
||||||
<string name="context_menu_copy_image_link">Kopiera bildadressen</string>
|
<string name="context_menu_copy_image_link">Kopiera bildadressen</string>
|
||||||
<!-- More from MainActivity -->
|
<!-- More from MainActivity -->
|
||||||
<string name="unable_to_load_image">Kunde inte ladda bilden</string>
|
<string name="unable_to_load_image">Kunde inte ladda bilden</string>
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
<string name="context_menu_save_image">Зберегти зображення</string>
|
<string name="context_menu_save_image">Зберегти зображення</string>
|
||||||
<string name="context_menu_share_image">Поділитися зображенням</string>
|
<string name="context_menu_share_image">Поділитися зображенням</string>
|
||||||
<string name="context_menu_open_external_browser">Відкрити у зовнішньому браузері…</string>
|
<string name="context_menu_open_external_browser">Відкрити у зовнішньому браузері…</string>
|
||||||
<string name="context_menu_copy_link">Копіювати адресу посилання у буфер обміну</string>
|
<string name="copy_link_to_clipboard">Копіювати адресу посилання у буфер обміну</string>
|
||||||
<string name="context_menu_copy_image_link">Копіювати зображення у буфер обміну</string>
|
<string name="context_menu_copy_image_link">Копіювати зображення у буфер обміну</string>
|
||||||
<!-- More from MainActivity -->
|
<!-- More from MainActivity -->
|
||||||
<string name="unable_to_load_image">Не вдалося завантажити зображення</string>
|
<string name="unable_to_load_image">Не вдалося завантажити зображення</string>
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
<string name="context_menu_save_image">儲存圖片</string>
|
<string name="context_menu_save_image">儲存圖片</string>
|
||||||
<string name="context_menu_share_image">分享圖片</string>
|
<string name="context_menu_share_image">分享圖片</string>
|
||||||
<string name="context_menu_open_external_browser">用外部瀏覽器開啟…</string>
|
<string name="context_menu_open_external_browser">用外部瀏覽器開啟…</string>
|
||||||
<string name="context_menu_copy_link">將連結網址複製到剪貼簿</string>
|
<string name="copy_link_to_clipboard">將連結網址複製到剪貼簿</string>
|
||||||
<string name="context_menu_copy_image_link">將圖片網址複製到剪貼簿</string>
|
<string name="context_menu_copy_image_link">將圖片網址複製到剪貼簿</string>
|
||||||
<!-- More from MainActivity -->
|
<!-- More from MainActivity -->
|
||||||
<string name="unable_to_load_image">無法載入圖片</string>
|
<string name="unable_to_load_image">無法載入圖片</string>
|
||||||
|
|
|
@ -127,4 +127,5 @@
|
||||||
<string name="pref_key__is_overview_statusbar_hidden" translatable="false">pref_key__is_overview_statusbar_hidden</string>
|
<string name="pref_key__is_overview_statusbar_hidden" translatable="false">pref_key__is_overview_statusbar_hidden</string>
|
||||||
<string name="pref_key__recreate_main_activity" translatable="false">pref_key__recreate_main_activity</string>
|
<string name="pref_key__recreate_main_activity" translatable="false">pref_key__recreate_main_activity</string>
|
||||||
<string name="pref_key__show_title" translatable="false">pref_key__show_title</string>
|
<string name="pref_key__show_title" translatable="false">pref_key__show_title</string>
|
||||||
|
<string name="pdf" translatable="false">PDF</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -70,7 +70,7 @@
|
||||||
<string name="context_menu_save_image">Save image</string>
|
<string name="context_menu_save_image">Save image</string>
|
||||||
<string name="context_menu_share_image">Share image</string>
|
<string name="context_menu_share_image">Share image</string>
|
||||||
<string name="context_menu_open_external_browser">Open in external browser…</string>
|
<string name="context_menu_open_external_browser">Open in external browser…</string>
|
||||||
<string name="context_menu_copy_link">Copy link address to clipboard</string>
|
<string name="copy_link_to_clipboard">Copy link address to clipboard</string>
|
||||||
<string name="context_menu_copy_image_link">Copy image address to clipboard</string>
|
<string name="context_menu_copy_image_link">Copy image address to clipboard</string>
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,4 +98,5 @@
|
||||||
<string name="pref_title__is_statusbar_hidden">Hide statusbar</string>
|
<string name="pref_title__is_statusbar_hidden">Hide statusbar</string>
|
||||||
<string name="pref_summary__show_title">Show title in the main view</string>
|
<string name="pref_summary__show_title">Show title in the main view</string>
|
||||||
<string name="pref_title__show_title">Show title</string>
|
<string name="pref_title__show_title">Show title</string>
|
||||||
|
<string name="launcher_shortcut">Launcher shortcut</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -3,16 +3,16 @@ It adds useful features to your networking experience:
|
||||||
|
|
||||||
⚡ Quick access to most diaspora* features
|
⚡ Quick access to most diaspora* features
|
||||||
👉 Share content to and from the app
|
👉 Share content to and from the app
|
||||||
🌎 Proxy support
|
🌎 Proxy support (Tor/Orbot supported)
|
||||||
📰 In-app-browser to view articles
|
📰 In-app-browser to view articles
|
||||||
🎨 Customizeable colors
|
🎨 Customizeable colors
|
||||||
🌆 Night/AMOLED mode
|
🌆 Night/AMOLED mode
|
||||||
🈴 Localized in many languages
|
🈴 Localized in many languages
|
||||||
✔️ Allows system independent language
|
✔️ Allows system independent language
|
||||||
|
|
||||||
|
|
||||||
<b>Support the project:</b>
|
<b>Support the project:</b>
|
||||||
✋ <a href="https://lonamiwebs.github.io/stringlate/translate?git=https%3A%2F%2Fgithub.com%2Fdiaspora-for-android%2Fdandelion.git&mail=gro.xobliam@@rentnasg">Translate using Stringlate</a>
|
✋ <a href="https://lonamiwebs.github.io/stringlate/translate?git=https%3A%2F%2Fgithub.com%2Fdiaspora-for-android%2Fdandelion.git&mail=gro.xobliam@@rentnasg">Translate using Stringlate</a>
|
||||||
✋ <a href="https://matrix.to/#/#dandelion:matrix.org">Join discussion on Matrix</a>
|
✋ <a href="https://matrix.to/#/#dandelion:matrix.org">Join discussion on Matrix</a>
|
||||||
✋ <a href="https://github.com/diaspora-for-android/dandelion#contributions">More information about contributions</a>
|
✋ <a href="https://github.com/diaspora-for-android/dandelion#contributions">More information about contributions</a>
|
||||||
|
✋ <a href="https://gsantner.net/android-contribution-guide/?packageid=com.github.dfa.diaspora_android&name=dandelion&web=https://github.com/diaspora-for-android/dandelion">Android Contribution Guide (gsantner blog)</a>
|
||||||
✋ <a href="http://gsantner.net/supportme?ref=dandelion&source=fdroid">Support main developer</a>
|
✋ <a href="http://gsantner.net/supportme?ref=dandelion&source=fdroid">Support main developer</a>
|
||||||
|
|
Loading…
Reference in a new issue