diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 63d1e119d..3dbff5663 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -389,6 +389,11 @@
android:exported="true">
+
+
+
diff --git a/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java b/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java
index d9524d2fe..762e46a3f 100644
--- a/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java
+++ b/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java
@@ -160,8 +160,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private static final int GROUP_EDIT = 6;
private static final int TAKE_PHOTO = 7;
private static final int RECORD_VIDEO = 8;
- private static final int PICK_LOCATION = 9; // TODO: i think, this can be deleted
- private static final int SMS_DEFAULT = 11; // TODO: i think, this can be deleted
+ private static final int PICK_WEBXDC = 9;
private GlideRequests glideRequests;
protected ComposeText composeText;
@@ -335,8 +334,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
public void onActivityResult(final int reqCode, int resultCode, Intent data) {
super.onActivityResult(reqCode, resultCode, data);
- if ((data == null && reqCode != TAKE_PHOTO && reqCode != RECORD_VIDEO && reqCode != SMS_DEFAULT) ||
- (resultCode != RESULT_OK && reqCode != SMS_DEFAULT))
+ if (resultCode != RESULT_OK || (data == null && reqCode != TAKE_PHOTO && reqCode != RECORD_VIDEO))
{
return;
}
@@ -372,6 +370,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
setMedia(data.getData(), docMediaType);
break;
+ case PICK_WEBXDC:
+ setMedia(data.getData(), MediaType.DOCUMENT);
+ break;
+
case PICK_CONTACT:
addAttachmentContactInfo(data.getIntExtra(AttachContactActivity.CONTACT_ID_EXTRA, 0));
break;
@@ -399,16 +401,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
break;
- case PICK_LOCATION:
- break;
-
case ScribbleActivity.SCRIBBLE_REQUEST_CODE:
setMedia(data.getData(), MediaType.IMAGE);
break;
-
- case SMS_DEFAULT:
- initializeSecurity(isSecureText, isDefaultSms);
- break;
}
}
@@ -931,6 +926,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
case AttachmentTypeSelector.RECORD_VIDEO:
attachmentManager.captureVideo(this, RECORD_VIDEO);
break;
+ case AttachmentTypeSelector.ADD_WEBXDC:
+ AttachmentManager.selectWebxdc(this, PICK_WEBXDC); break;
}
}
diff --git a/src/main/java/org/thoughtcrime/securesms/WebxdcStoreActivity.java b/src/main/java/org/thoughtcrime/securesms/WebxdcStoreActivity.java
new file mode 100644
index 000000000..5c3ae66e1
--- /dev/null
+++ b/src/main/java/org/thoughtcrime/securesms/WebxdcStoreActivity.java
@@ -0,0 +1,136 @@
+package org.thoughtcrime.securesms;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.MenuItem;
+import android.webkit.WebResourceRequest;
+import android.webkit.WebResourceResponse;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.Toast;
+
+import androidx.appcompat.app.ActionBar;
+
+import com.b44t.messenger.DcContext;
+import com.b44t.messenger.rpc.HttpResponse;
+import com.b44t.messenger.rpc.Rpc;
+import com.b44t.messenger.rpc.RpcException;
+
+import org.thoughtcrime.securesms.connect.DcHelper;
+import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
+import org.thoughtcrime.securesms.util.MediaUtil;
+import org.thoughtcrime.securesms.util.Prefs;
+
+import java.io.ByteArrayInputStream;
+import java.util.HashMap;
+
+public class WebxdcStoreActivity extends PassphraseRequiredActionBarActivity {
+ private static final String TAG = WebxdcStoreActivity.class.getSimpleName();
+
+ private DcContext dcContext;
+ private Rpc rpc;
+
+ @Override
+ protected void onCreate(Bundle state, boolean ready) {
+ setContentView(R.layout.web_view_activity);
+ rpc = DcHelper.getRpc(this);
+ dcContext = DcHelper.getContext(this);
+ WebView webView = findViewById(R.id.webview);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setTitle(R.string.webxdc_apps);
+ }
+
+ webView.setWebViewClient(new WebViewClient() {
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ String ext = MediaUtil.getFileExtensionFromUrl(Uri.parse(url).getPath());
+ if ("xdc".equals(ext)) {
+ try {
+ HttpResponse httpResponse = rpc.getHttpResponse(dcContext.getAccountId(), url);
+ Uri uri = PersistentBlobProvider.getInstance().create(WebxdcStoreActivity.this, httpResponse.getBlob(), "application/octet-stream", "app.xdc");
+ Intent intent = new Intent();
+ intent.setData(uri);
+ setResult(Activity.RESULT_OK, intent);
+ finish();
+ } catch (RpcException e) {
+ e.printStackTrace();
+ Toast.makeText(WebxdcStoreActivity.this, "Error: " + e.getMessage(), Toast.LENGTH_LONG).show();
+ }
+ } else {
+ WebViewActivity.openUrlInBrowser(WebxdcStoreActivity.this, url);
+ }
+ return true;
+ }
+
+ @TargetApi(Build.VERSION_CODES.N)
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
+ return shouldOverrideUrlLoading(view, request.getUrl().toString());
+ }
+
+ @Override
+ public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
+ return interceptRequest(request.getUrl().toString());
+ }
+ });
+
+ WebSettings webSettings = webView.getSettings();
+ webSettings.setJavaScriptEnabled(true);
+ webSettings.setAllowFileAccess(false);
+ webSettings.setAllowContentAccess(false);
+ webSettings.setGeolocationEnabled(false);
+ webSettings.setAllowFileAccessFromFileURLs(false);
+ webSettings.setAllowUniversalAccessFromFileURLs(false);
+ webSettings.setDatabaseEnabled(true);
+ webSettings.setDomStorageEnabled(true);
+ webView.setNetworkAvailable(true); // this does not block network but sets `window.navigator.isOnline` in js land
+
+ webView.loadUrl(Prefs.getWebxdcStoreUrl(this));
+ }
+
+ private WebResourceResponse interceptRequest(String url) {
+ WebResourceResponse res = null;
+ try {
+ if (url == null) {
+ throw new Exception("no url specified");
+ }
+ HttpResponse httpResponse = rpc.getHttpResponse(dcContext.getAccountId(), url);
+ String mimeType = httpResponse.getMimetype();
+ if (mimeType == null) {
+ mimeType = "application/octet-stream";
+ }
+
+ ByteArrayInputStream data = new ByteArrayInputStream(httpResponse.getBlob());
+ res = new WebResourceResponse(mimeType, httpResponse.getEncoding(), data);
+ } catch (Exception e) {
+ e.printStackTrace();
+ ByteArrayInputStream data = new ByteArrayInputStream(("Could not load apps. Are you online?\n\n" + e.getMessage()).getBytes());
+ res = new WebResourceResponse("text/plain", "UTF-8", data);
+ }
+
+ HashMap headers = new HashMap<>();
+ headers.put("access-control-allow-origin", "*");
+ res.setResponseHeaders(headers);
+ return res;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ super.onOptionsItemSelected(item);
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/src/main/java/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java b/src/main/java/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java
index c2c517e08..fcbce7eb2 100644
--- a/src/main/java/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java
+++ b/src/main/java/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java
@@ -41,6 +41,7 @@ public class AttachmentTypeSelector extends PopupWindow {
public static final int TAKE_PHOTO = 5;
public static final int ADD_LOCATION = 6;
public static final int RECORD_VIDEO = 7;
+ public static final int ADD_WEBXDC = 8;
private static final int ANIMATION_DURATION = 300;
@@ -53,7 +54,7 @@ public class AttachmentTypeSelector extends PopupWindow {
//private final @NonNull ImageView cameraButton;
private final @NonNull ImageView videoButton;
private final @NonNull ImageView locationButton;
- private final @NonNull ImageView closeButton;
+ private final @NonNull ImageView webxdcButton;
private @Nullable View currentAnchor;
private @Nullable AttachmentClickedListener listener;
@@ -76,7 +77,7 @@ public class AttachmentTypeSelector extends PopupWindow {
//this.cameraButton = ViewUtil.findById(layout, R.id.camera_button);
this.videoButton = ViewUtil.findById(layout, R.id.record_video_button);
this.locationButton = ViewUtil.findById(layout, R.id.location_button);
- this.closeButton = ViewUtil.findById(layout, R.id.close_button);
+ this.webxdcButton = ViewUtil.findById(layout, R.id.webxdc_button);
this.imageButton.setOnClickListener(new PropagatingClickListener(ADD_GALLERY));
this.videoChatButton.setOnClickListener(new PropagatingClickListener(INVITE_VIDEO_CHAT));
@@ -85,7 +86,7 @@ public class AttachmentTypeSelector extends PopupWindow {
//this.cameraButton.setOnClickListener(new PropagatingClickListener(TAKE_PHOTO));
this.videoButton.setOnClickListener(new PropagatingClickListener(RECORD_VIDEO));
this.locationButton.setOnClickListener(new PropagatingClickListener(ADD_LOCATION));
- this.closeButton.setOnClickListener(new CloseClickListener());
+ this.webxdcButton.setOnClickListener(new PropagatingClickListener(ADD_WEBXDC));
this.recentRail.setListener(new RecentPhotoSelectedListener());
if (!Prefs.isLocationStreamingEnabled(context)) {
@@ -140,8 +141,8 @@ public class AttachmentTypeSelector extends PopupWindow {
animateButtonIn(contactButton, ANIMATION_DURATION / 3);
animateButtonIn(locationButton, ANIMATION_DURATION / 4);
animateButtonIn(documentButton, ANIMATION_DURATION / 4);
+ animateButtonIn(webxdcButton, 0);
animateButtonIn(videoChatButton, 0);
- animateButtonIn(closeButton, 0);
}
@Override
@@ -289,13 +290,6 @@ public class AttachmentTypeSelector extends PopupWindow {
}
- private class CloseClickListener implements View.OnClickListener {
- @Override
- public void onClick(View v) {
- dismiss();
- }
- }
-
public interface AttachmentClickedListener {
public void onClick(int type);
public void onQuickAttachment(Uri uri);
diff --git a/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java
index e70ce5a09..b8ebf4cfb 100644
--- a/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java
+++ b/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java
@@ -50,6 +50,7 @@ import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.MediaPreviewActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.ShareLocationDialog;
+import org.thoughtcrime.securesms.WebxdcStoreActivity;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.UriAttachment;
import org.thoughtcrime.securesms.audio.AudioSlidePlayer;
@@ -451,6 +452,11 @@ public class AttachmentManager {
selectMediaType(activity, "*/*", null, requestCode);
}
+ public static void selectWebxdc(Activity activity, int requestCode) {
+ Intent intent = new Intent(activity, WebxdcStoreActivity.class);
+ activity.startActivityForResult(intent, requestCode);
+ }
+
public static void selectGallery(Activity activity, int requestCode) {
// to enable camera roll,
// we're asking for "gallery permissions" also on newer systems that do not strictly require that.
diff --git a/src/main/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java b/src/main/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java
index eb2fd6f92..48319922e 100644
--- a/src/main/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java
+++ b/src/main/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java
@@ -42,6 +42,7 @@ import org.thoughtcrime.securesms.connect.DcEventCenter;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.mms.AttachmentManager;
import org.thoughtcrime.securesms.permissions.Permissions;
+import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.util.ScreenLockUtil;
import org.thoughtcrime.securesms.util.StorageUtil;
import org.thoughtcrime.securesms.util.StreamUtil;
@@ -134,6 +135,10 @@ public class AdvancedPreferenceFragment extends ListSummaryPreferenceFragment
webrtcInstance.setOnPreferenceClickListener(new WebrtcInstanceListener());
updateWebrtcSummary();
+ Preference webxdcStore = this.findPreference(Prefs.WEBXDC_STORE_URL_PREF);
+ webxdcStore.setOnPreferenceClickListener(new WebxdcStoreUrlListener());
+ updateWebxdcStoreSummary();
+
Preference developerModeEnabled = this.findPreference("pref_developer_mode_enabled");
developerModeEnabled.setOnPreferenceChangeListener((preference, newValue) -> {
WebView.setWebContentsDebuggingEnabled((Boolean) newValue);
@@ -280,6 +285,29 @@ public class AdvancedPreferenceFragment extends ListSummaryPreferenceFragment
}
}
+ private class WebxdcStoreUrlListener implements Preference.OnPreferenceClickListener {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ View gl = View.inflate(getActivity(), R.layout.single_line_input, null);
+ EditText inputField = gl.findViewById(R.id.input_field);
+ inputField.setHint(Prefs.DEFAULT_WEBXDC_STORE_URL);
+ inputField.setText(Prefs.getWebxdcStoreUrl(getActivity()));
+ inputField.setSelection(inputField.getText().length());
+ inputField.setInputType(TYPE_TEXT_VARIATION_URI);
+ new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.webxdc_store_url)
+ .setMessage(R.string.webxdc_store_url_explain)
+ .setView(gl)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, (dlg, btn) -> {
+ Prefs.setWebxdcStoreUrl(getActivity(), inputField.getText().toString());
+ updateWebxdcStoreSummary();
+ })
+ .show();
+ return true;
+ }
+ }
+
private void updateWebrtcSummary() {
Preference webrtcInstance = this.findPreference("pref_webrtc_instance");
if (webrtcInstance != null) {
@@ -288,6 +316,13 @@ public class AdvancedPreferenceFragment extends ListSummaryPreferenceFragment
}
}
+ private void updateWebxdcStoreSummary() {
+ Preference preference = this.findPreference(Prefs.WEBXDC_STORE_URL_PREF);
+ if (preference != null) {
+ preference.setSummary(Prefs.getWebxdcStoreUrl(getActivity()));
+ }
+ }
+
private void openRegistrationActivity() {
Intent intent = new Intent(getActivity(), RegistrationActivity.class);
startActivity(intent);
diff --git a/src/main/java/org/thoughtcrime/securesms/util/Prefs.java b/src/main/java/org/thoughtcrime/securesms/util/Prefs.java
index bf6b2f97a..f0bdc039b 100644
--- a/src/main/java/org/thoughtcrime/securesms/util/Prefs.java
+++ b/src/main/java/org/thoughtcrime/securesms/util/Prefs.java
@@ -62,6 +62,8 @@ public class Prefs {
public static final boolean ALWAYS_LOAD_REMOTE_CONTENT_DEFAULT = false;
public static final String LAST_DEVICE_MSG_LABEL = "pref_last_device_msg_id";
+ public static final String WEBXDC_STORE_URL_PREF = "pref_webxdc_store_url";
+ public static final String DEFAULT_WEBXDC_STORE_URL = "https://webxdc.org/apps/";
public enum VibrateState {
DEFAULT(0), ENABLED(1), DISABLED(2);
@@ -144,6 +146,15 @@ public class Prefs {
return getStringPreference(context, THEME_PREF, DynamicTheme.systemThemeAvailable() ? DynamicTheme.SYSTEM : DynamicTheme.LIGHT);
}
+ public static String getWebxdcStoreUrl(Context context) {
+ return getStringPreference(context, WEBXDC_STORE_URL_PREF, DEFAULT_WEBXDC_STORE_URL);
+ }
+
+ public static void setWebxdcStoreUrl(Context context, String url) {
+ if (url == null || url.trim().isEmpty() || DEFAULT_WEBXDC_STORE_URL.equals(url)) url = null;
+ setStringPreference(context, WEBXDC_STORE_URL_PREF, url);
+ }
+
public static void setPromptedDozeMsgId(Context context, int msg_id) {
setIntegerPreference(context, PROMPTED_DOZE_MSG_ID_PREF, msg_id);
}
diff --git a/src/main/res/drawable/baseline_apps_24.xml b/src/main/res/drawable/baseline_apps_24.xml
new file mode 100644
index 000000000..18dae5c30
--- /dev/null
+++ b/src/main/res/drawable/baseline_apps_24.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/main/res/layout/attachment_type_selector.xml b/src/main/res/layout/attachment_type_selector.xml
index f74b01408..b521f9049 100644
--- a/src/main/res/layout/attachment_type_selector.xml
+++ b/src/main/res/layout/attachment_type_selector.xml
@@ -78,58 +78,50 @@
-
+
+
+
+
+
+
+
+
+ android:id="@+id/webxdc_button"
+ android:layout_width="53dp"
+ android:layout_height="53dp"
+ android:src="@drawable/baseline_apps_24"
+ android:scaleType="center"
+ android:contentDescription="@string/webxdc_apps"
+ app:circleColor="@color/apps_icon"
+ />
-
-
-
-
-
-
-
-
+ android:text="@string/webxdc_apps"/>
@@ -169,26 +161,30 @@
-
+ android:visibility="visible"
+ android:contentDescription="@string/location"
+ app:circleColor="@color/location_icon"/>
+ android:visibility="visible"
+ style="@style/AttachmentTypeLabel"
+ android:text="@string/location"/>
@@ -216,30 +212,14 @@
-
-
-
-
-
-
+
+
-
diff --git a/src/main/res/values/core_colors.xml b/src/main/res/values/core_colors.xml
index 8b999a404..1c91c5e66 100644
--- a/src/main/res/values/core_colors.xml
+++ b/src/main/res/values/core_colors.xml
@@ -13,6 +13,7 @@
#608496
#a47ad9
#66BB6A
+ #77bdc4
#ffffff
#F9FAFA
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 2168f54fc..070912c84 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -190,6 +190,8 @@
App
Apps
+ App Picker URL
+ If set, the URL will be used as the App Picker instead of the default one
Unknown
Green
diff --git a/src/main/res/xml/preferences_advanced.xml b/src/main/res/xml/preferences_advanced.xml
index d87fdd31d..479ab2333 100644
--- a/src/main/res/xml/preferences_advanced.xml
+++ b/src/main/res/xml/preferences_advanced.xml
@@ -26,6 +26,10 @@
android:title="@string/videochat_instance"
android:summary="@string/none"/>
+
+