Detailed changes
  
  
    
    @@ -45,6 +45,11 @@
             android:label="@string/invite_to_app"
             android:exported="false"
             android:launchMode="singleTask" />
+        <activity
+            android:name="com.cheogram.android.WebxdcStore"
+            android:label="Widgets"
+            android:exported="false"
+            android:launchMode="standard" />
         <activity
             android:name=".ui.ImportBackupActivity"
             android:label="@string/restore_backup"
  
  
  
    
    @@ -0,0 +1,111 @@
+package com.cheogram.android;
+
+// Based on code from https://codeberg.org/monocles/monocles_chat/raw/commit/master/src/main/java/de/monocles/chat/WebXDCStore.java
+
+import static android.view.View.VISIBLE;
+import static eu.siacs.conversations.ui.ActionBarActivity.configureActionBar;
+import static eu.siacs.conversations.utils.AccountUtils.MANAGE_ACCOUNT_ACTIVITY;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.text.Html;
+import android.text.method.LinkMovementMethod;
+import android.view.MenuItem;
+import android.webkit.DownloadListener;
+import android.webkit.URLUtil;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.services.ChannelDiscoveryService;
+import eu.siacs.conversations.ui.Activities;
+import eu.siacs.conversations.ui.XmppActivity;
+import eu.siacs.conversations.databinding.ActivityWebxdcStoreBinding;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AlertDialog;
+import androidx.databinding.DataBindingUtil;
+
+import android.annotation.SuppressLint;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class WebxdcStore extends XmppActivity {
+    @SuppressLint("SetJavaScriptEnabled")
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final ActivityWebxdcStoreBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_webxdc_store);
+        Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
+        setSupportActionBar(binding.toolbar);
+        configureActionBar(getSupportActionBar());
+
+        //getSupportActionBar().setDisplayShowHomeEnabled(false);
+        //getSupportActionBar().setDisplayHomeAsUpEnabled(false);
+        WebView webView = findViewById(R.id.web);
+        String URL = "https://webxdc.org/apps/";
+        if (isNetworkAvailable(this)) {
+            webView.loadUrl(URL);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+                webView.setForceDarkAllowed(true);
+            }
+            webView.getSettings().setJavaScriptEnabled(true);
+            webView.getSettings().setDomStorageEnabled(true);
+            webView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
+            webView.setWebViewClient(new WebViewClient() {
+                @Override
+                public boolean shouldOverrideUrlLoading(WebView view, String url) {
+                    view.loadUrl(url);
+                    return true;
+                }
+            });
+            webView.setDownloadListener(new DownloadListener() {
+                @Override
+                public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength) {
+                    final var intent = new Intent();
+                    intent.setData(Uri.parse(url));
+                    setResult(Activity.RESULT_OK, intent);
+                    finish();
+                }
+            });
+        } else {
+            Toast.makeText(this, R.string.account_status_no_internet, Toast.LENGTH_LONG).show();
+        }
+    }
+
+    @Override
+    protected void refreshUiReal() {
+
+    }
+
+    @Override
+    protected void onBackendConnected() {
+    }
+
+    //check for internet connection
+    private boolean isNetworkAvailable(Context context) {
+        ConnectivityManager connectivity = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        if (connectivity != null) {
+            NetworkInfo[] info = connectivity.getAllNetworkInfo();
+            if (info != null) {
+                for (NetworkInfo anInfo : info) {
+                    if (anInfo.getState() == NetworkInfo.State.CONNECTED) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+}
  
  
  
    
    @@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (c) 2024, Arne-BrΓΌn Vogelsang All rights reserved.
+  ~
+  ~ Redistribution and use in source and binary forms, with or without modification,
+  ~ are permitted provided that the following conditions are met:
+  ~
+  ~ 1. Redistributions of source code must retain the above copyright notice, this
+  ~ list of conditions and the following disclaimer.
+  ~
+  ~ 2. Redistributions in binary form must reproduce the above copyright notice,
+  ~ this list of conditions and the following disclaimer in the documentation and/or
+  ~ other materials provided with the distribution.
+  ~
+  ~ 3. Neither the name of the copyright holder nor the names of its contributors
+  ~ may be used to endorse or promote products derived from this software without
+  ~ specific prior written permission.
+  ~
+  ~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+  ~ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  ~ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  ~ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+  ~ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+  ~ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  ~ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+  ~ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  ~ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  ~ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+  -->
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+        <com.google.android.material.appbar.AppBarLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <com.google.android.material.appbar.MaterialToolbar
+                android:id="@+id/toolbar"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:minHeight="?attr/actionBarSize" />
+
+        </com.google.android.material.appbar.AppBarLayout>
+
+        <WebView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:id="@+id/web"/>
+
+    </LinearLayout>
+</layout>
  
  
  
    
    @@ -89,6 +89,7 @@ import androidx.viewpager.widget.ViewPager;
 import com.cheogram.android.BobTransfer;
 import com.cheogram.android.EmojiSearch;
 import com.cheogram.android.WebxdcPage;
+import com.cheogram.android.WebxdcStore;
 
 import com.google.android.material.color.MaterialColors;
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
@@ -217,6 +218,7 @@ public class ConversationFragment extends XmppFragment
     public static final int REQUEST_START_AUDIO_CALL = 0x213;
     public static final int REQUEST_START_VIDEO_CALL = 0x214;
     public static final int REQUEST_SAVE_STICKER = 0x215;
+    public static final int REQUEST_WEBXDC_STORE = 0x216;
     public static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
     public static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
     public static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303;
@@ -1138,6 +1140,10 @@ public class ConversationFragment extends XmppFragment
 
     private void handlePositiveActivityResult(int requestCode, final Intent data) {
         switch (requestCode) {
+            case REQUEST_WEBXDC_STORE:
+                mediaPreviewAdapter.addMediaPreviews(Attachment.of(activity, data.getData(), Attachment.Type.FILE));
+                toggleInputMethod();
+                break;
             case REQUEST_SAVE_STICKER:
                 final DocumentFile df = DocumentFile.fromSingleUri(activity, data.getData());
                 final File f = savingAsSticker;
@@ -1982,6 +1988,10 @@ public class ConversationFragment extends XmppFragment
             case R.id.attach_location:
                 handleAttachmentSelection(item);
                 break;
+            case R.id.attach_webxdc:
+                final Intent intent = new Intent(getActivity(), WebxdcStore.class);
+                startActivityForResult(intent, REQUEST_WEBXDC_STORE);
+                break;
             case R.id.attach_subject:
                 binding.textinputSubject.setVisibility(binding.textinputSubject.getVisibility() == View.GONE ? View.VISIBLE : View.GONE);
                 break;
  
  
  
    
    @@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+      
+    <path android:fillColor="@android:color/white" android:pathData="M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.1,3 19,3zM12.01,18c-0.7,0 -1.26,-0.56 -1.26,-1.26c0,-0.71 0.56,-1.25 1.26,-1.25c0.71,0 1.25,0.54 1.25,1.25C13.25,17.43 12.72,18 12.01,18zM15.02,10.6c-0.76,1.11 -1.48,1.46 -1.87,2.17c-0.16,0.29 -0.22,0.48 -0.22,1.41h-1.82c0,-0.49 -0.08,-1.29 0.31,-1.98c0.49,-0.87 1.42,-1.39 1.96,-2.16c0.57,-0.81 0.25,-2.33 -1.37,-2.33c-1.06,0 -1.58,0.8 -1.8,1.48L8.56,8.49C9.01,7.15 10.22,6 11.99,6c1.48,0 2.49,0.67 3.01,1.52C15.44,8.24 15.7,9.59 15.02,10.6z"/>
+    
+</vector>
  
  
  
    
    @@ -62,6 +62,11 @@
                 android:icon="@drawable/ic_location_pin_24dp"
                 android:title="@string/send_location" />
 
+            <item
+                android:id="@+id/attach_webxdc"
+                android:icon="@drawable/ic_help_center_24dp"
+                android:title="Choose Widget" />
+
             <item
                 android:id="@+id/attach_subject"
                 android:icon="@drawable/subject"