allow to select as app to open .xdc files, remove zhv

This commit is contained in:
adbenitez
2023-07-20 02:27:52 +02:00
parent 98bf26dd5a
commit b65b535cdc
5 changed files with 20 additions and 325 deletions
+14 -37
View File
@@ -65,6 +65,20 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:exported="true"> android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.GET_CONTENT"/>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="*"/>
<data android:scheme="http"/>
<data android:scheme="https"/>
<data android:scheme="content"/>
<data android:scheme="file"/>
<data android:mimeType="*/*" />
<data android:mimeType="application/octet-stream" />
</intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.SEND" /> <action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT"/>
@@ -336,43 +350,6 @@
<activity android:name=".map.MapActivity" /> <activity android:name=".map.MapActivity" />
<activity android:name=".ZhvActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.GET_CONTENT"/>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="*"/>
<data android:scheme="http"/>
<data android:scheme="https"/>
<data android:scheme="content"/>
<data android:scheme="file"/>
<data android:mimeType="*/*" />
<data android:mimeType="application/octet-stream" />
<data android:pathPattern=".*\\.html.zip" />
<data android:pathPattern=".*\\..*\\.html.zip" />
<data android:pathPattern=".*\\..*\\..*\\.html.zip" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.html.zip" />
<data android:pathPattern=".*\\.htmlzip" />
<data android:pathPattern=".*\\..*\\.htmlzip" />
<data android:pathPattern=".*\\..*\\..*\\.htmlzip" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.htmlzip" />
<data android:mimeType="text/html"/>
<data android:pathPattern=".*\\.html" />
<data android:pathPattern=".*\\..*\\.html" />
<data android:pathPattern=".*\\..*\\..*\\.html" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.html" />
<data android:mimeType="text/markdown"/>
<data android:pathPattern=".*\\.md" />
<data android:pathPattern=".*\\..*\\.md" />
<data android:pathPattern=".*\\..*\\..*\\.md" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.md" />
</intent-filter>
</activity>
<service android:name=".connect.KeepAliveService" android:enabled="true" /> <service android:name=".connect.KeepAliveService" android:enabled="true" />
<service android:name=".geolocation.LocationBackgroundService" /> <service android:name=".geolocation.LocationBackgroundService" />
File diff suppressed because one or more lines are too long
@@ -22,11 +22,9 @@ DeltaLab was created with a focus on usability, good user experience, and saving
<li>When you select a message you have additional options to remove/add the sender from the current group easily</li> <li>When you select a message you have additional options to remove/add the sender from the current group easily</li>
<li>Voice messages have more aggressive compression in "worse quality" mode to save data plan</li> <li>Voice messages have more aggressive compression in "worse quality" mode to save data plan</li>
<li>Default configuration changed: automatic download of messages limited to 160KB</li> <li>Default configuration changed: automatic download of messages limited to 160KB</li>
<li>Messages in groups are ignored if the sender is not a group member</li>
<li>Account's display name is shown in the app's title bar instead of the name of the app</li> <li>Account's display name is shown in the app's title bar instead of the name of the app</li>
<li>Your avatar is visible to other users in Mailing Lists</li> <li>Your avatar is visible to other users in Mailing Lists</li>
<li>Can open/display mardown (*.md) attachments</li>
<li>Can open/display *.htmlzip attachments</li>
<li>Can be selected as application when clicking/opening links, this is useful to share the link with web grabber bots</li> <li>Can be selected as application when clicking/opening links, this is useful to share the link with web grabber bots</li>
<li>Can be selected as app to open .xdc files</li>
<li>For mini-apps developers: there are some differences in the WebXDC API, check https://github.com/adbenitez/deltalab-android/#webxdc</li> <li>For mini-apps developers: there are some differences in the WebXDC API, check https://github.com/adbenitez/deltalab-android/#webxdc</li>
</ul> </ul>
@@ -124,6 +124,11 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity implement
streamExtras.add(uri); streamExtras.add(uri);
} else if (getIntent().getParcelableArrayListExtra(Intent.EXTRA_STREAM) != null) { } else if (getIntent().getParcelableArrayListExtra(Intent.EXTRA_STREAM) != null) {
streamExtras = getIntent().getParcelableArrayListExtra(Intent.EXTRA_STREAM); streamExtras = getIntent().getParcelableArrayListExtra(Intent.EXTRA_STREAM);
} else {
Uri uri = getIntent().getData();
if (uri != null) {
streamExtras.add(uri);
}
} }
if (needsFilePermission(streamExtras)) { if (needsFilePermission(streamExtras)) {
@@ -1,284 +0,0 @@
package org.thoughtcrime.securesms;
import android.annotation.SuppressLint;
import android.app.Activity;
import java.util.Objects;
import android.os.Bundle;
import java.io.InputStream;
import java.io.IOException;
import android.net.Uri;
import android.view.Menu;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.webkit.JavascriptInterface;
import android.widget.SearchView;
import android.widget.Toast;
import android.webkit.WebView;
import android.webkit.WebSettings;
import android.webkit.WebChromeClient;
import androidx.annotation.NonNull;
import androidx.webkit.WebSettingsCompat;
import androidx.webkit.WebViewFeature;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipEntry;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
public class ZhvActivity extends PassphraseRequiredActionBarActivity implements View.OnClickListener {
private static final String MD_TEXT = "MD_TEXT";
private static final String FIRST_TIME_INIT = "FirstTimeInit";
private static final String IS_SEARCHING = "Searching";
private static final String TITLE_WEB = "TitleWeb";
private static final int ZHV_VERSION_CODE = 8;
private boolean firstTimeInit = true;
private boolean isSearching = false;
private JsObject jsObject;
private String titleWEB = "ZHV";
protected WebView webView;
private final DynamicTheme dynamicTheme = new DynamicTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
@Override
protected void onPreCreate() {
dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this);
}
@Override
protected void onCreate(Bundle savedInstanceState, boolean ready) {
super.onCreate(savedInstanceState, ready);
if (savedInstanceState != null) {
firstTimeInit = savedInstanceState.getBoolean(FIRST_TIME_INIT, true);
isSearching = savedInstanceState.getBoolean(IS_SEARCHING, false);
titleWEB = savedInstanceState.getString(TITLE_WEB, titleWEB);
String text = savedInstanceState.getString(MD_TEXT, "");
if (text.equals("")) {
jsObject = new JsObject("", false);
} else {
jsObject = new JsObject(text, true);
}
}
setContentView(R.layout.activity_zhv);
webView = findViewById(R.id.webview);
if (firstTimeInit) {
handleIntent();
} else {
registerForContextMenu(webView);
webView.restoreState(savedInstanceState);
configureWebView(webView);
this.setTitle(titleWEB);
}
}
@SuppressLint("SetJavaScriptEnabled")
public void configureWebView(WebView web) {
WebSettings webSettings = web.getSettings();
webSettings.setBlockNetworkLoads(true);
webSettings.setJavaScriptEnabled(false);
webSettings.setSupportZoom(true);
webSettings.setBuiltInZoomControls(true);
webSettings.setDomStorageEnabled(true);
web.addJavascriptInterface(jsObject, "zhv");
web.setWebChromeClient(new WebChromeClient() {
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
ZhvActivity.this.titleWEB = title;
ZhvActivity.this.setTitle(title);
}
});
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
if (DynamicTheme.isDarkTheme(this)) {
WebSettingsCompat.setForceDark(webSettings, WebSettingsCompat.FORCE_DARK_ON);
} else {
WebSettingsCompat.setForceDark(webSettings, WebSettingsCompat.FORCE_DARK_OFF);
}
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.search_menu, menu);
final SearchView searchView = (SearchView) menu.findItem(R.id.search_menu_item).getActionView();
searchView.setImeOptions(EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS | EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NAVIGATE_NEXT);
searchView.setOnSearchClickListener(this);
searchView.setSubmitButtonEnabled(true);
searchView.setOnCloseListener(() -> {
isSearching = false;
return false;
});
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
webView.findNext(true);
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
webView.findAllAsync(newText);
return true;
}
});
return super.onCreateOptionsMenu(menu);
}
@Override
public void onBackPressed() {
if (isSearching) {
SearchView searchView = findViewById(R.id.search_menu_item);
//if text closes it completly
searchView.setIconified(true);
searchView.setIconified(true);
isSearching = false;
} else {
super.onBackPressed();
}
}
private void handleIntent() {
Uri uri = getIntent().getData();
if (uri == null) {
tellUserThatCouldNotOpenFile();
return;
}
String text = null;
Boolean isMarkdown = false;
try {
InputStream inputStream = getContentResolver().openInputStream(uri);
if (uri.getPath() != null) {
String path = uri.getPath();
if (path.endsWith(".html.zip") || path.endsWith(".htmlzip")) {
// zip html mode
text = getStringFromZip(inputStream);
} else {
// load file into memory (html or markdown)
ByteArrayOutputStream result = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {
result.write(buffer, 0, length);
}
text = result.toString();
if(path.endsWith(".md")){
// markdown mode
isMarkdown = true;
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
if (text == null) {
tellUserThatCouldNotOpenFile();
return;
}
jsObject = new JsObject(text, isMarkdown);
configureWebView(webView);
webView.loadData("", "text/html", null);
if(isMarkdown){
// markdown mode
webView.loadUrl("file:///android_asset/markdown-reader.html");
} else {
// html mode
webView.loadDataWithBaseURL("file://index.html", text, "text/html", null, null);
}
firstTimeInit = false;
}
private void tellUserThatCouldNotOpenFile() {
Toast.makeText(this, getString(R.string.could_not_open_file), Toast.LENGTH_SHORT).show();
}
public static String getStringFromZip(InputStream stream) throws IOException {
ByteArrayOutputStream fout = new ByteArrayOutputStream();
if (!unpackZip(stream, fout)) {
throw new IOException();
}
return fout.toString();
}
private static boolean unpackZip(InputStream is, ByteArrayOutputStream fout) {
ZipInputStream zis;
try {
zis = new ZipInputStream(new BufferedInputStream(is));
ZipEntry ze;
byte[] buffer = new byte[1024];
int count;
while ((ze = zis.getNextEntry()) != null) {
if (ze.isDirectory())
continue;
//android 4.1 Compatibility fix Objects class access do a crash
if (!ze.getName().contentEquals("index.html"))
continue;
while ((count = zis.read(buffer)) != -1) {
fout.write(buffer, 0, count);
}
zis.closeEntry();
}
zis.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
webView.saveState(outState);
outState.putBoolean(IS_SEARCHING, isSearching);
outState.putBoolean(FIRST_TIME_INIT, firstTimeInit);
outState.putString(TITLE_WEB, titleWEB);
outState.putString(MD_TEXT, jsObject.getMarkdown());
super.onSaveInstanceState(outState);
}
@Override
public void onClick(View v) {
isSearching = true;
}
class JsObject {
String contentText = "";
boolean isMarkdownReader = false;
public JsObject(String contentText, boolean isMarkdownReader) {
this.contentText = contentText;
this.isMarkdownReader = isMarkdownReader;
}
@JavascriptInterface
public int getVersion() { return ZHV_VERSION_CODE; }
@NonNull
@JavascriptInterface
public String toString() { return "[ZippedHTMLViewer Object]"; }
@JavascriptInterface
public String getMarkdown() {
if (isMarkdownReader) {
return contentText;
} else {
return "";
}
}
}
}