Merge branch 'first-swap-overhaul' into 'master'
First swap overhaul Closes #656, #612, and #586 See merge request fdroid/fdroidclient!671
This commit is contained in:
		
						commit
						714a44ad5d
					
				@ -40,7 +40,7 @@ dependencies {
 | 
			
		||||
    compile "info.guardianproject.panic:panic:0.5"
 | 
			
		||||
    compile 'commons-io:commons-io:2.5'
 | 
			
		||||
    compile 'commons-net:commons-net:3.5'
 | 
			
		||||
    compile 'org.openhab.jmdns:jmdns:3.4.2'
 | 
			
		||||
    compile 'org.jmdns:jmdns:3.5.3'
 | 
			
		||||
    compile 'ch.acra:acra:4.9.1'
 | 
			
		||||
    compile 'io.reactivex:rxjava:1.1.0'
 | 
			
		||||
    compile 'io.reactivex:rxandroid:0.23.0'
 | 
			
		||||
@ -137,7 +137,8 @@ if (!hasProperty('sourceDeps')) {
 | 
			
		||||
                'info.guardianproject.panic:panic:a7ed9439826db2e9901649892cf9afbe76f00991b768d8f4c26332d7c9406cb2',
 | 
			
		||||
                'io.reactivex:rxandroid:35c1a90f8c1f499db3c1f3d608e1f191ac8afddb10c02dd91ef04c03a0a4bcda',
 | 
			
		||||
                'io.reactivex:rxjava:2c162afd78eba217cdfee78b60e85d3bfb667db61e12bc95e3cf2ddc5beeadf6',
 | 
			
		||||
                'org.openhab.jmdns:jmdns:7a4b34b5606bbd2aff7fdfe629edcb0416fccd367fb59a099f210b9aba4f0bce',
 | 
			
		||||
                'org.jmdns:jmdns:24e7e3a50a579136400e8c9b0750399eb3c7558918bdf52c0ffa5e0fa5aad503',
 | 
			
		||||
                'org.slf4j:slf4j-api:e56288031f5e60652c06e7bb6e9fa410a61231ab54890f7b708fc6adc4107c5b',
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -181,7 +182,6 @@ def preDexEnabled = "true".equals(System.getProperty("pre-dex", "true"))
 | 
			
		||||
android {
 | 
			
		||||
    compileSdkVersion 24
 | 
			
		||||
    buildToolsVersion '25.0.3'
 | 
			
		||||
    useLibrary 'org.apache.http.legacy'
 | 
			
		||||
 | 
			
		||||
    buildTypes {
 | 
			
		||||
        // use proguard on debug too since we have unknowingly broken
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							@ -7,6 +7,9 @@
 | 
			
		||||
-dontwarn com.android.support.test.**
 | 
			
		||||
 | 
			
		||||
-dontwarn javax.naming.**
 | 
			
		||||
-dontwarn org.slf4j.**
 | 
			
		||||
-dontnote org.apache.http.**
 | 
			
		||||
-dontnote android.net.http.**
 | 
			
		||||
-dontnote android.support.**
 | 
			
		||||
-dontnote **ILicensingService
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,12 +4,12 @@ import android.os.Parcel;
 | 
			
		||||
import android.os.Parcelable;
 | 
			
		||||
import android.text.TextUtils;
 | 
			
		||||
 | 
			
		||||
import javax.jmdns.ServiceInfo;
 | 
			
		||||
import javax.jmdns.impl.util.ByteWrangler;
 | 
			
		||||
import java.net.Inet4Address;
 | 
			
		||||
import java.net.Inet6Address;
 | 
			
		||||
import java.net.UnknownHostException;
 | 
			
		||||
 | 
			
		||||
import javax.jmdns.ServiceInfo;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The ServiceInfo class needs to be serialized in order to be sent as an Android broadcast.
 | 
			
		||||
 * In order to make it Parcelable (or Serializable for that matter), there are some package-scope
 | 
			
		||||
@ -32,7 +32,7 @@ public class FDroidServiceInfo extends ServiceInfoImpl implements Parcelable {
 | 
			
		||||
        if (data == null || data.length == 0) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        String fingerprint = this.readUTF(data, 0, data.length);
 | 
			
		||||
        String fingerprint = ByteWrangler.readUTF(data, 0, data.length);
 | 
			
		||||
        if (TextUtils.isEmpty(fingerprint)) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -47,6 +47,7 @@ import org.fdroid.fdroid.data.Repo;
 | 
			
		||||
import org.fdroid.fdroid.data.RepoProvider;
 | 
			
		||||
import org.fdroid.fdroid.data.Schema;
 | 
			
		||||
import org.fdroid.fdroid.installer.InstallManagerService;
 | 
			
		||||
import org.fdroid.fdroid.net.BluetoothDownloader;
 | 
			
		||||
import org.fdroid.fdroid.net.ConnectivityMonitorService;
 | 
			
		||||
import org.fdroid.fdroid.views.main.MainActivity;
 | 
			
		||||
 | 
			
		||||
@ -332,7 +333,7 @@ public class UpdateService extends IntentService {
 | 
			
		||||
        boolean forcedUpdate = false;
 | 
			
		||||
        String address = null;
 | 
			
		||||
        if (intent != null) {
 | 
			
		||||
            address = intent.getStringExtra(EXTRA_ADDRESS);
 | 
			
		||||
            address = intent.getStringExtra(EXTRA_ADDRESS); // TODO switch to Intent.setData()
 | 
			
		||||
            manualUpdate = intent.getBooleanExtra(EXTRA_MANUAL_UPDATE, false);
 | 
			
		||||
            forcedUpdate = intent.getBooleanExtra(EXTRA_FORCED_UPDATE, false);
 | 
			
		||||
        }
 | 
			
		||||
@ -340,7 +341,9 @@ public class UpdateService extends IntentService {
 | 
			
		||||
        try {
 | 
			
		||||
            // See if it's time to actually do anything yet...
 | 
			
		||||
            int netState = ConnectivityMonitorService.getNetworkState(this);
 | 
			
		||||
            if (netState == ConnectivityMonitorService.FLAG_NET_UNAVAILABLE) {
 | 
			
		||||
            if (address != null && address.startsWith(BluetoothDownloader.SCHEME)) {
 | 
			
		||||
                Utils.debugLog(TAG, "skipping internet check, this is bluetooth");
 | 
			
		||||
            } else if (netState == ConnectivityMonitorService.FLAG_NET_UNAVAILABLE) {
 | 
			
		||||
                Utils.debugLog(TAG, "No internet, cannot update");
 | 
			
		||||
                if (manualUpdate) {
 | 
			
		||||
                    sendNoInternetToast();
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,10 @@
 | 
			
		||||
package org.fdroid.fdroid.localrepo;
 | 
			
		||||
 | 
			
		||||
import android.annotation.SuppressLint;
 | 
			
		||||
import android.app.Notification;
 | 
			
		||||
import android.app.PendingIntent;
 | 
			
		||||
import android.app.Service;
 | 
			
		||||
import android.bluetooth.BluetoothAdapter;
 | 
			
		||||
import android.content.BroadcastReceiver;
 | 
			
		||||
import android.content.ContentValues;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
@ -10,7 +12,7 @@ import android.content.Intent;
 | 
			
		||||
import android.content.IntentFilter;
 | 
			
		||||
import android.content.SharedPreferences;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.net.http.AndroidHttpClient;
 | 
			
		||||
import android.net.wifi.WifiManager;
 | 
			
		||||
import android.os.AsyncTask;
 | 
			
		||||
import android.os.IBinder;
 | 
			
		||||
import android.support.annotation.IntDef;
 | 
			
		||||
@ -20,11 +22,6 @@ import android.support.v4.app.NotificationCompat;
 | 
			
		||||
import android.support.v4.content.LocalBroadcastManager;
 | 
			
		||||
import android.text.TextUtils;
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
import org.apache.http.HttpHost;
 | 
			
		||||
import org.apache.http.NameValuePair;
 | 
			
		||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
 | 
			
		||||
import org.apache.http.client.methods.HttpPost;
 | 
			
		||||
import org.apache.http.message.BasicNameValuePair;
 | 
			
		||||
import org.fdroid.fdroid.FDroidApp;
 | 
			
		||||
import org.fdroid.fdroid.Preferences;
 | 
			
		||||
import org.fdroid.fdroid.R;
 | 
			
		||||
@ -47,13 +44,14 @@ import rx.android.schedulers.AndroidSchedulers;
 | 
			
		||||
import rx.schedulers.Schedulers;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.UnsupportedEncodingException;
 | 
			
		||||
import java.io.OutputStream;
 | 
			
		||||
import java.io.OutputStreamWriter;
 | 
			
		||||
import java.lang.annotation.Retention;
 | 
			
		||||
import java.lang.annotation.RetentionPolicy;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.net.HttpURLConnection;
 | 
			
		||||
import java.net.URL;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.Timer;
 | 
			
		||||
import java.util.TimerTask;
 | 
			
		||||
@ -67,10 +65,13 @@ import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
public class SwapService extends Service {
 | 
			
		||||
 | 
			
		||||
    private static final String TAG = "SwapService";
 | 
			
		||||
 | 
			
		||||
    private static final String SHARED_PREFERENCES = "swap-state";
 | 
			
		||||
    private static final String KEY_APPS_TO_SWAP = "appsToSwap";
 | 
			
		||||
    private static final String KEY_BLUETOOTH_ENABLED = "bluetoothEnabled";
 | 
			
		||||
    private static final String KEY_WIFI_ENABLED = "wifiEnabled";
 | 
			
		||||
    private static final String KEY_BLUETOOTH_ENABLED_BEFORE_SWAP = "bluetoothEnabledBeforeSwap";
 | 
			
		||||
    private static final String KEY_WIFI_ENABLED_BEFORE_SWAP = "wifiEnabledBeforeSwap";
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    private final Set<String> appsToSwap = new HashSet<>();
 | 
			
		||||
@ -80,6 +81,10 @@ public class SwapService extends Service {
 | 
			
		||||
     */
 | 
			
		||||
    private static final ConcurrentHashMap<String, App> INSTALLED_APPS = new ConcurrentHashMap<>();
 | 
			
		||||
 | 
			
		||||
    private static SharedPreferences swapPreferences;
 | 
			
		||||
    private static BluetoothAdapter bluetoothAdapter;
 | 
			
		||||
    private static WifiManager wifiManager;
 | 
			
		||||
 | 
			
		||||
    public static void stop(Context context) {
 | 
			
		||||
        Intent intent = new Intent(context, SwapService.class);
 | 
			
		||||
        context.stopService(intent);
 | 
			
		||||
@ -93,16 +98,6 @@ public class SwapService extends Service {
 | 
			
		||||
        INSTALLED_APPS.put(packageName, app);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Where relevant, the state of the swap process will be saved to disk using preferences.
 | 
			
		||||
     * Note that this is not always useful, for example saving the "current wifi network" is
 | 
			
		||||
     * bound to cause trouble when the user opens the swap process again and is connected to
 | 
			
		||||
     * a different network.
 | 
			
		||||
     */
 | 
			
		||||
    private SharedPreferences persistence() {
 | 
			
		||||
        return getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ==========================================================
 | 
			
		||||
    //                 Search for peers to swap
 | 
			
		||||
    // ==========================================================
 | 
			
		||||
@ -208,55 +203,37 @@ public class SwapService extends Service {
 | 
			
		||||
        UpdateService.updateRepoNow(this, peer.getRepoAddress());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SuppressLint("StaticFieldLeak")
 | 
			
		||||
    private void askServerToSwapWithUs(final Repo repo) {
 | 
			
		||||
        askServerToSwapWithUs(repo.address);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void askServerToSwapWithUs(final String address) {
 | 
			
		||||
        new AsyncTask<Void, Void, Void>() {
 | 
			
		||||
            @Override
 | 
			
		||||
            protected Void doInBackground(Void... args) {
 | 
			
		||||
                Uri repoUri = Uri.parse(address);
 | 
			
		||||
                String swapBackUri = Utils.getLocalRepoUri(FDroidApp.repo).toString();
 | 
			
		||||
 | 
			
		||||
                AndroidHttpClient client = AndroidHttpClient.newInstance("F-Droid", SwapService.this);
 | 
			
		||||
                HttpPost request = new HttpPost("/request-swap");
 | 
			
		||||
                HttpHost host = new HttpHost(repoUri.getHost(), repoUri.getPort(), repoUri.getScheme());
 | 
			
		||||
 | 
			
		||||
                HttpURLConnection conn = null;
 | 
			
		||||
                try {
 | 
			
		||||
                    Utils.debugLog(TAG, "Asking server at " + address + " to swap with us in return (by POSTing to \"/request-swap\" with repo \"" + swapBackUri + "\")...");
 | 
			
		||||
                    populatePostParams(swapBackUri, request);
 | 
			
		||||
                    client.execute(host, request);
 | 
			
		||||
                    URL url = new URL(repo.address.replace("/fdroid/repo", "/request-swap"));
 | 
			
		||||
                    conn = (HttpURLConnection) url.openConnection();
 | 
			
		||||
                    conn.setRequestMethod("POST");
 | 
			
		||||
                    conn.setDoInput(true);
 | 
			
		||||
                    conn.setDoOutput(true);
 | 
			
		||||
 | 
			
		||||
                    OutputStream outputStream = conn.getOutputStream();
 | 
			
		||||
                    OutputStreamWriter writer = new OutputStreamWriter(outputStream);
 | 
			
		||||
                    writer.write("repo=" + swapBackUri);
 | 
			
		||||
                    writer.flush();
 | 
			
		||||
                    writer.close();
 | 
			
		||||
                    outputStream.close();
 | 
			
		||||
 | 
			
		||||
                    int responseCode = conn.getResponseCode();
 | 
			
		||||
                    Utils.debugLog(TAG, "Asking server at " + repo.address + " to swap with us in return (by " +
 | 
			
		||||
                            "POSTing to \"/request-swap\" with repo \"" + swapBackUri + "\"): " + responseCode);
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    notifyOfErrorOnUiThread();
 | 
			
		||||
                    Log.e(TAG, "Error while asking server to swap with us", e);
 | 
			
		||||
                } finally {
 | 
			
		||||
                    client.close();
 | 
			
		||||
                    conn.disconnect();
 | 
			
		||||
                }
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            private void populatePostParams(String swapBackUri, HttpPost request) throws UnsupportedEncodingException {
 | 
			
		||||
                List<NameValuePair> params = new ArrayList<>();
 | 
			
		||||
                params.add(new BasicNameValuePair("repo", swapBackUri));
 | 
			
		||||
                UrlEncodedFormEntity encodedParams = new UrlEncodedFormEntity(params);
 | 
			
		||||
                request.setEntity(encodedParams);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            private void notifyOfErrorOnUiThread() {
 | 
			
		||||
                // TODO: Broadcast error message so that whoever wants to can display a relevant
 | 
			
		||||
                // message in the UI. This service doesn't understand the concept of UI.
 | 
			
		||||
                /*runOnUiThread(new Runnable() {
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public void run() {
 | 
			
		||||
                        Toast.makeText(
 | 
			
		||||
                                SwapService.this,
 | 
			
		||||
                                R.string.swap_reciprocate_failed,
 | 
			
		||||
                                Toast.LENGTH_LONG
 | 
			
		||||
                        ).show();
 | 
			
		||||
                    }
 | 
			
		||||
                });*/
 | 
			
		||||
            }
 | 
			
		||||
        }.execute();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -333,7 +310,7 @@ public class SwapService extends Service {
 | 
			
		||||
    // ==========================================
 | 
			
		||||
 | 
			
		||||
    private void persistAppsToSwap() {
 | 
			
		||||
        persistence().edit().putString(KEY_APPS_TO_SWAP, serializePackages(appsToSwap)).apply();
 | 
			
		||||
        swapPreferences.edit().putString(KEY_APPS_TO_SWAP, serializePackages(appsToSwap)).apply();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -389,30 +366,36 @@ public class SwapService extends Service {
 | 
			
		||||
        persistAppsToSwap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // =============================================================
 | 
			
		||||
    //   Remember which swap technologies a user used in the past
 | 
			
		||||
    // =============================================================
 | 
			
		||||
 | 
			
		||||
    private final BroadcastReceiver receiveSwapStatusChanged = new BroadcastReceiver() {
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onReceive(Context context, Intent intent) {
 | 
			
		||||
            Utils.debugLog(TAG, "Remembering that Bluetooth swap " + (bluetoothSwap.isConnected() ? "IS" : "is NOT") +
 | 
			
		||||
                    " connected and WiFi swap " + (wifiSwap.isConnected() ? "IS" : "is NOT") + " connected.");
 | 
			
		||||
            persistence().edit()
 | 
			
		||||
                    .putBoolean(KEY_BLUETOOTH_ENABLED, bluetoothSwap.isConnected())
 | 
			
		||||
                    .putBoolean(KEY_WIFI_ENABLED, wifiSwap.isConnected())
 | 
			
		||||
                    .apply();
 | 
			
		||||
    public static boolean getBluetoothVisibleUserPreference() {
 | 
			
		||||
        return swapPreferences.getBoolean(SwapService.KEY_BLUETOOTH_ENABLED, false);
 | 
			
		||||
    }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    private boolean wasBluetoothEnabled() {
 | 
			
		||||
        return persistence().getBoolean(KEY_BLUETOOTH_ENABLED, false);
 | 
			
		||||
    public static void putBluetoothVisibleUserPreference(boolean visible) {
 | 
			
		||||
        swapPreferences.edit().putBoolean(SwapService.KEY_BLUETOOTH_ENABLED, visible).apply();
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    private boolean wasWifiEnabled() {
 | 
			
		||||
        return persistence().getBoolean(KEY_WIFI_ENABLED, false);
 | 
			
		||||
    public static boolean getWifiVisibleUserPreference() {
 | 
			
		||||
        return swapPreferences.getBoolean(SwapService.KEY_WIFI_ENABLED, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void putWifiVisibleUserPreference(boolean visible) {
 | 
			
		||||
        swapPreferences.edit().putBoolean(SwapService.KEY_WIFI_ENABLED, visible).apply();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean wasBluetoothEnabledBeforeSwap() {
 | 
			
		||||
        return swapPreferences.getBoolean(SwapService.KEY_BLUETOOTH_ENABLED_BEFORE_SWAP, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void putBluetoothEnabledBeforeSwap(boolean visible) {
 | 
			
		||||
        swapPreferences.edit().putBoolean(SwapService.KEY_BLUETOOTH_ENABLED_BEFORE_SWAP, visible).apply();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean wasWifiEnabledBeforeSwap() {
 | 
			
		||||
        return swapPreferences.getBoolean(SwapService.KEY_WIFI_ENABLED_BEFORE_SWAP, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void putWifiEnabledBeforeSwap(boolean visible) {
 | 
			
		||||
        swapPreferences.edit().putBoolean(SwapService.KEY_WIFI_ENABLED_BEFORE_SWAP, visible).apply();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -452,9 +435,6 @@ public class SwapService extends Service {
 | 
			
		||||
        return wifiSwap.isConnected() && wifiSwap.getBonjour().isConnected();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static final String ACTION_PEER_FOUND = "org.fdroid.fdroid.SwapManager.ACTION_PEER_FOUND";
 | 
			
		||||
    public static final String EXTRA_PEER = "EXTRA_PEER";
 | 
			
		||||
 | 
			
		||||
    // ===============================================================
 | 
			
		||||
    //        Old SwapService stuff being merged into that.
 | 
			
		||||
    // ===============================================================
 | 
			
		||||
@ -503,32 +483,39 @@ public class SwapService extends Service {
 | 
			
		||||
 | 
			
		||||
        CacheSwapAppsService.startCaching(this);
 | 
			
		||||
 | 
			
		||||
        SharedPreferences preferences = getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE);
 | 
			
		||||
        swapPreferences = getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE);
 | 
			
		||||
 | 
			
		||||
        appsToSwap.addAll(deserializePackages(preferences.getString(KEY_APPS_TO_SWAP, "")));
 | 
			
		||||
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
 | 
			
		||||
        if (bluetoothAdapter != null) {
 | 
			
		||||
            SwapService.putBluetoothEnabledBeforeSwap(bluetoothAdapter.isEnabled());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
 | 
			
		||||
        if (wifiManager != null) {
 | 
			
		||||
            SwapService.putWifiEnabledBeforeSwap(wifiManager.isWifiEnabled());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        appsToSwap.addAll(deserializePackages(swapPreferences.getString(KEY_APPS_TO_SWAP, "")));
 | 
			
		||||
        bluetoothSwap = BluetoothSwap.create(this);
 | 
			
		||||
        wifiSwap = new WifiSwap(this);
 | 
			
		||||
        wifiSwap = new WifiSwap(this, wifiManager);
 | 
			
		||||
 | 
			
		||||
        Preferences.get().registerLocalRepoHttpsListeners(httpsEnabledListener);
 | 
			
		||||
 | 
			
		||||
        LocalBroadcastManager.getInstance(this).registerReceiver(onWifiChange, new IntentFilter(WifiStateChangeService.BROADCAST));
 | 
			
		||||
        LocalBroadcastManager.getInstance(this).registerReceiver(onWifiChange,
 | 
			
		||||
                new IntentFilter(WifiStateChangeService.BROADCAST));
 | 
			
		||||
 | 
			
		||||
        IntentFilter filter = new IntentFilter(BLUETOOTH_STATE_CHANGE);
 | 
			
		||||
        filter.addAction(WIFI_STATE_CHANGE);
 | 
			
		||||
        LocalBroadcastManager.getInstance(this).registerReceiver(receiveSwapStatusChanged, filter);
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        if (wasBluetoothEnabled()) {
 | 
			
		||||
        if (getBluetoothVisibleUserPreference()) {
 | 
			
		||||
            Utils.debugLog(TAG, "Previously the user enabled Bluetooth swap, so enabling again automatically.");
 | 
			
		||||
            bluetoothSwap.startInBackground();
 | 
			
		||||
        }
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        if (wasWifiEnabled()) {
 | 
			
		||||
            Utils.debugLog(TAG, "Previously the user enabled WiFi swap, so enabling again automatically.");
 | 
			
		||||
            wifiSwap.startInBackground();
 | 
			
		||||
            bluetoothSwap.startInBackground(); // TODO replace with Intent to SwapService
 | 
			
		||||
        } else {
 | 
			
		||||
            Utils.debugLog(TAG, "WiFi was NOT enabled last time user swapped, so starting with WiFi not visible.");
 | 
			
		||||
            Utils.debugLog(TAG, "Bluetooth was NOT enabled last time user swapped, starting not visible.");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (getWifiVisibleUserPreference()) {
 | 
			
		||||
            Utils.debugLog(TAG, "Previously the user enabled WiFi swap, so enabling again automatically.");
 | 
			
		||||
            wifiSwap.startInBackground(); // TODO replace with Intent to SwapService
 | 
			
		||||
        } else {
 | 
			
		||||
            Utils.debugLog(TAG, "WiFi was NOT enabled last time user swapped, starting not visible.");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -549,7 +536,14 @@ public class SwapService extends Service {
 | 
			
		||||
        Utils.debugLog(TAG, "Destroying service, will disable swapping if required, and unregister listeners.");
 | 
			
		||||
        Preferences.get().unregisterLocalRepoHttpsListeners(httpsEnabledListener);
 | 
			
		||||
        LocalBroadcastManager.getInstance(this).unregisterReceiver(onWifiChange);
 | 
			
		||||
        LocalBroadcastManager.getInstance(this).unregisterReceiver(receiveSwapStatusChanged);
 | 
			
		||||
 | 
			
		||||
        if (!SwapService.wasBluetoothEnabledBeforeSwap()) {
 | 
			
		||||
            bluetoothAdapter.disable();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!SwapService.wasWifiEnabledBeforeSwap()) {
 | 
			
		||||
            wifiManager.setWifiEnabled(false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //TODO getBluetoothSwap().stopInBackground();
 | 
			
		||||
        getWifiSwap().stopInBackground();
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,6 @@ import android.content.IntentFilter;
 | 
			
		||||
import android.support.annotation.NonNull;
 | 
			
		||||
import android.support.annotation.Nullable;
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
 | 
			
		||||
import org.fdroid.fdroid.Utils;
 | 
			
		||||
import org.fdroid.fdroid.localrepo.SwapService;
 | 
			
		||||
import org.fdroid.fdroid.net.bluetooth.BluetoothServer;
 | 
			
		||||
@ -59,8 +58,8 @@ public final class BluetoothSwap extends SwapType {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public synchronized void start() {
 | 
			
		||||
 | 
			
		||||
        if (isConnected()) {
 | 
			
		||||
            Utils.debugLog(TAG, "already running, quitting start()");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -171,10 +170,12 @@ public final class BluetoothSwap extends SwapType {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void start() { }
 | 
			
		||||
        public void start() {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void stop() { }
 | 
			
		||||
        public void stop() {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        protected String getBroadcastAction() {
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ package org.fdroid.fdroid.localrepo.type;
 | 
			
		||||
 | 
			
		||||
import android.annotation.SuppressLint;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.net.wifi.WifiManager;
 | 
			
		||||
import android.os.Handler;
 | 
			
		||||
import android.os.Looper;
 | 
			
		||||
import android.os.Message;
 | 
			
		||||
@ -31,10 +32,12 @@ public class WifiSwap extends SwapType {
 | 
			
		||||
    private Handler webServerThreadHandler;
 | 
			
		||||
    private LocalHTTPD localHttpd;
 | 
			
		||||
    private final BonjourBroadcast bonjourBroadcast;
 | 
			
		||||
    private final WifiManager wifiManager;
 | 
			
		||||
 | 
			
		||||
    public WifiSwap(Context context) {
 | 
			
		||||
    public WifiSwap(Context context, WifiManager wifiManager) {
 | 
			
		||||
        super(context);
 | 
			
		||||
        bonjourBroadcast = new BonjourBroadcast(context);
 | 
			
		||||
        this.wifiManager = wifiManager;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected String getBroadcastAction() {
 | 
			
		||||
@ -47,6 +50,8 @@ public class WifiSwap extends SwapType {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void start() {
 | 
			
		||||
        wifiManager.setWifiEnabled(true);
 | 
			
		||||
 | 
			
		||||
        Utils.debugLog(TAG, "Preparing swap webserver.");
 | 
			
		||||
        sendBroadcast(SwapService.EXTRA_STARTING);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -14,15 +14,27 @@ import org.fdroid.fdroid.net.bluetooth.httpish.Response;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Download from a Bluetooth swap repo.  Example URI:
 | 
			
		||||
 * {@code bluetooth://84-CF-BF-8B-3E-34/fdroid/repo}
 | 
			
		||||
 */
 | 
			
		||||
public class BluetoothDownloader extends Downloader {
 | 
			
		||||
 | 
			
		||||
    private static final String TAG = "BluetoothDownloader";
 | 
			
		||||
 | 
			
		||||
    public static final String SCHEME = "bluetooth";
 | 
			
		||||
 | 
			
		||||
    private final BluetoothConnection connection;
 | 
			
		||||
    private FileDetails fileDetails;
 | 
			
		||||
    private final String sourcePath;
 | 
			
		||||
 | 
			
		||||
    public static boolean isBluetoothUri(Uri uri) {
 | 
			
		||||
        return SCHEME.equals(uri.getScheme())
 | 
			
		||||
                && Pattern.matches("([0-9A-F]{2}-)+[0-9A-F]{2}", uri.getHost());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public BluetoothDownloader(Uri uri, File destFile) throws IOException {
 | 
			
		||||
        super(uri, destFile);
 | 
			
		||||
        String macAddress = uri.getHost().replace("-", ":");
 | 
			
		||||
 | 
			
		||||
@ -122,16 +122,23 @@ public class HttpDownloader extends Downloader {
 | 
			
		||||
        cacheTag = connection.getHeaderField(HEADER_FIELD_ETAG);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private boolean isSwapUrl() {
 | 
			
		||||
        String host = sourceUrl.getHost();
 | 
			
		||||
        return sourceUrl.getPort() > 1023 // only root can use <= 1023, so never a swap repo
 | 
			
		||||
    public static boolean isSwapUrl(Uri uri) {
 | 
			
		||||
        return isSwapUrl(uri.getHost(), uri.getPort());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean isSwapUrl(URL url) {
 | 
			
		||||
        return isSwapUrl(url.getHost(), url.getPort());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean isSwapUrl(String host, int port) {
 | 
			
		||||
        return port > 1023 // only root can use <= 1023, so never a swap repo
 | 
			
		||||
                && host.matches("[0-9.]+") // host must be an IP address
 | 
			
		||||
                && FDroidApp.subnetInfo.isInRange(host); // on the same subnet as we are
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private HttpURLConnection getConnection() throws SocketTimeoutException, IOException {
 | 
			
		||||
        HttpURLConnection connection;
 | 
			
		||||
        if (isSwapUrl()) {
 | 
			
		||||
        if (isSwapUrl(sourceUrl)) {
 | 
			
		||||
            // swap never works with a proxy, its unrouted IP on the same subnet
 | 
			
		||||
            connection = (HttpURLConnection) sourceUrl.openConnection();
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
@ -66,7 +66,7 @@ public class BluetoothServer extends Thread {
 | 
			
		||||
    public void run() {
 | 
			
		||||
 | 
			
		||||
        isRunning = true;
 | 
			
		||||
        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
 | 
			
		||||
        final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            serverSocket = adapter.listenUsingInsecureRfcommWithServiceRecord("FDroid App Swap", BluetoothConstants.fdroidUuid());
 | 
			
		||||
@ -83,6 +83,11 @@ public class BluetoothServer extends Thread {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!adapter.isEnabled()) {
 | 
			
		||||
                Utils.debugLog(TAG, "User disabled Bluetooth from outside, stopping.");
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                BluetoothSocket clientSocket = serverSocket.accept();
 | 
			
		||||
                if (clientSocket != null) {
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,149 @@
 | 
			
		||||
package org.fdroid.fdroid.views.swap;
 | 
			
		||||
 | 
			
		||||
import android.annotation.SuppressLint;
 | 
			
		||||
import android.annotation.TargetApi;
 | 
			
		||||
import android.content.BroadcastReceiver;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.content.IntentFilter;
 | 
			
		||||
import android.graphics.LightingColorFilter;
 | 
			
		||||
import android.support.annotation.ColorRes;
 | 
			
		||||
import android.support.annotation.NonNull;
 | 
			
		||||
import android.support.v4.content.LocalBroadcastManager;
 | 
			
		||||
import android.text.TextUtils;
 | 
			
		||||
import android.util.AttributeSet;
 | 
			
		||||
import android.view.Menu;
 | 
			
		||||
import android.view.MenuInflater;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.widget.Button;
 | 
			
		||||
import android.widget.ImageView;
 | 
			
		||||
import android.widget.ScrollView;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
import org.fdroid.fdroid.FDroidApp;
 | 
			
		||||
import org.fdroid.fdroid.Preferences;
 | 
			
		||||
import org.fdroid.fdroid.QrGenAsyncTask;
 | 
			
		||||
import org.fdroid.fdroid.R;
 | 
			
		||||
import org.fdroid.fdroid.Utils;
 | 
			
		||||
import org.fdroid.fdroid.localrepo.SwapService;
 | 
			
		||||
import org.fdroid.fdroid.net.WifiStateChangeService;
 | 
			
		||||
import org.fdroid.fdroid.views.swap.device.camera.CameraCharacteristicsChecker;
 | 
			
		||||
 | 
			
		||||
public class SendFDroidView extends ScrollView implements SwapWorkflowActivity.InnerView {
 | 
			
		||||
 | 
			
		||||
    private static final String TAG = "SendFDroidView";
 | 
			
		||||
 | 
			
		||||
    public SendFDroidView(Context context) {
 | 
			
		||||
        super(context);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public SendFDroidView(Context context, AttributeSet attrs) {
 | 
			
		||||
        super(context, attrs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public SendFDroidView(Context context, AttributeSet attrs, int defStyleAttr) {
 | 
			
		||||
        super(context, attrs, defStyleAttr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @TargetApi(21)
 | 
			
		||||
    public SendFDroidView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
 | 
			
		||||
        super(context, attrs, defStyleAttr, defStyleRes);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private SwapWorkflowActivity getActivity() {
 | 
			
		||||
        return (SwapWorkflowActivity) getContext();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onFinishInflate() {
 | 
			
		||||
        super.onFinishInflate();
 | 
			
		||||
        setUIFromWifi();
 | 
			
		||||
        setUpWarningMessageQrScan();
 | 
			
		||||
 | 
			
		||||
        ImageView qrImage = (ImageView) findViewById(R.id.wifi_qr_code);
 | 
			
		||||
 | 
			
		||||
        // Replace all blacks with the background blue.
 | 
			
		||||
        qrImage.setColorFilter(new LightingColorFilter(0xffffffff, getResources().getColor(R.color.swap_blue)));
 | 
			
		||||
 | 
			
		||||
        Button useBluetooth = (Button) findViewById(R.id.btn_use_bluetooth);
 | 
			
		||||
        useBluetooth.setOnClickListener(new Button.OnClickListener() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onClick(View v) {
 | 
			
		||||
                getActivity().showIntro();
 | 
			
		||||
                getActivity().sendFDroidBluetooth();
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
 | 
			
		||||
                onWifiStateChanged, new IntentFilter(WifiStateChangeService.BROADCAST));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setUpWarningMessageQrScan() {
 | 
			
		||||
        final View qrWarningMessage = findViewById(R.id.warning_qr_scanner);
 | 
			
		||||
        final boolean hasAutofocus = CameraCharacteristicsChecker.getInstance(getContext()).hasAutofocus();
 | 
			
		||||
        final int visiblity = hasAutofocus ? GONE : VISIBLE;
 | 
			
		||||
        qrWarningMessage.setVisibility(visiblity);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Remove relevant listeners/receivers/etc so that they do not receive and process events
 | 
			
		||||
     * when this view is not in use.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onDetachedFromWindow() {
 | 
			
		||||
        super.onDetachedFromWindow();
 | 
			
		||||
 | 
			
		||||
        LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(onWifiStateChanged);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean buildMenu(Menu menu, @NonNull MenuInflater inflater) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getStep() {
 | 
			
		||||
        return SwapService.STEP_INTRO;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getPreviousStep() {
 | 
			
		||||
        return SwapService.STEP_INTRO;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ColorRes
 | 
			
		||||
    public int getToolbarColour() {
 | 
			
		||||
        return R.color.swap_blue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getToolbarTitle() {
 | 
			
		||||
        return getResources().getString(R.string.swap_send_fdroid);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SuppressLint("HardwareIds")
 | 
			
		||||
    private void setUIFromWifi() {
 | 
			
		||||
        if (TextUtils.isEmpty(FDroidApp.repo.address)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        String scheme = Preferences.get().isLocalRepoHttpsEnabled() ? "https://" : "http://";
 | 
			
		||||
 | 
			
		||||
        // the fingerprint is not useful on the button label
 | 
			
		||||
        String qrUriString = scheme + FDroidApp.ipAddressString + ":" + FDroidApp.port;
 | 
			
		||||
        TextView ipAddressView = (TextView) findViewById(R.id.device_ip_address);
 | 
			
		||||
        ipAddressView.setText(qrUriString);
 | 
			
		||||
 | 
			
		||||
        Utils.debugLog(TAG, "Encoded swap URI in QR Code: " + qrUriString);
 | 
			
		||||
        new QrGenAsyncTask(getActivity(), R.id.wifi_qr_code).execute(qrUriString);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private final BroadcastReceiver onWifiStateChanged = new BroadcastReceiver() {
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onReceive(Context context, Intent intent) {
 | 
			
		||||
            setUIFromWifi();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -324,6 +324,7 @@ public class StartSwapView extends RelativeLayout implements SwapWorkflowActivit
 | 
			
		||||
                viewBluetoothId.setVisibility(View.GONE);
 | 
			
		||||
                Utils.debugLog(TAG, "Received onCheckChanged(false) for Bluetooth swap, Bluetooth swap disabled successfully.");
 | 
			
		||||
            }
 | 
			
		||||
            SwapService.putBluetoothVisibleUserPreference(isChecked);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@ -344,7 +345,8 @@ public class StartSwapView extends RelativeLayout implements SwapWorkflowActivit
 | 
			
		||||
        // and the Bonjour service at the same time. Technically swap will work fine without
 | 
			
		||||
        // Bonjour, and that is more of a convenience. Thus, we should show feedback once wifi
 | 
			
		||||
        // is ready, even if Bonjour is not yet.
 | 
			
		||||
        LocalBroadcastManager.getInstance(getContext()).registerReceiver(onWifiSwapStateChanged, new IntentFilter(SwapService.WIFI_STATE_CHANGE));
 | 
			
		||||
        LocalBroadcastManager.getInstance(getContext()).registerReceiver(onWifiSwapStateChanged,
 | 
			
		||||
                new IntentFilter(SwapService.WIFI_STATE_CHANGE));
 | 
			
		||||
 | 
			
		||||
        viewWifiNetwork.setOnClickListener(new OnClickListener() {
 | 
			
		||||
            @Override
 | 
			
		||||
@ -426,6 +428,7 @@ public class StartSwapView extends RelativeLayout implements SwapWorkflowActivit
 | 
			
		||||
                Utils.debugLog(TAG, "Received onCheckChanged(false) for WiFi swap, disabling WiFi swap in background thread.");
 | 
			
		||||
                getManager().getWifiSwap().stopInBackground();
 | 
			
		||||
            }
 | 
			
		||||
            SwapService.putWifiVisibleUserPreference(isChecked);
 | 
			
		||||
            uiUpdateWifiNetwork();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
package org.fdroid.fdroid.views.swap;
 | 
			
		||||
 | 
			
		||||
import android.annotation.TargetApi;
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.app.PendingIntent;
 | 
			
		||||
import android.bluetooth.BluetoothAdapter;
 | 
			
		||||
@ -12,8 +13,10 @@ import android.content.ServiceConnection;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.net.wifi.WifiManager;
 | 
			
		||||
import android.os.AsyncTask;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.os.IBinder;
 | 
			
		||||
import android.provider.Settings;
 | 
			
		||||
import android.support.annotation.ColorRes;
 | 
			
		||||
import android.support.annotation.LayoutRes;
 | 
			
		||||
import android.support.annotation.NonNull;
 | 
			
		||||
@ -29,10 +32,9 @@ import android.view.MenuInflater;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
 | 
			
		||||
import cc.mvdan.accesspoint.WifiApControl;
 | 
			
		||||
import com.google.zxing.integration.android.IntentIntegrator;
 | 
			
		||||
import com.google.zxing.integration.android.IntentResult;
 | 
			
		||||
 | 
			
		||||
import org.fdroid.fdroid.BuildConfig;
 | 
			
		||||
import org.fdroid.fdroid.FDroidApp;
 | 
			
		||||
import org.fdroid.fdroid.NfcHelper;
 | 
			
		||||
@ -47,6 +49,8 @@ import org.fdroid.fdroid.installer.Installer;
 | 
			
		||||
import org.fdroid.fdroid.localrepo.LocalRepoManager;
 | 
			
		||||
import org.fdroid.fdroid.localrepo.SwapService;
 | 
			
		||||
import org.fdroid.fdroid.localrepo.peers.Peer;
 | 
			
		||||
import org.fdroid.fdroid.net.BluetoothDownloader;
 | 
			
		||||
import org.fdroid.fdroid.net.HttpDownloader;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
@ -57,8 +61,6 @@ import java.util.Set;
 | 
			
		||||
import java.util.Timer;
 | 
			
		||||
import java.util.TimerTask;
 | 
			
		||||
 | 
			
		||||
import cc.mvdan.accesspoint.WifiApControl;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This activity will do its best to show the most relevant screen about swapping to the user.
 | 
			
		||||
 * The problem comes when there are two competing goals - 1) Show the user a list of apps from another
 | 
			
		||||
@ -111,6 +113,7 @@ public class SwapWorkflowActivity extends AppCompatActivity {
 | 
			
		||||
    private static final int REQUEST_BLUETOOTH_ENABLE_FOR_SWAP = 2;
 | 
			
		||||
    private static final int REQUEST_BLUETOOTH_DISCOVERABLE = 3;
 | 
			
		||||
    private static final int REQUEST_BLUETOOTH_ENABLE_FOR_SEND = 4;
 | 
			
		||||
    private static final int REQUEST_WRITE_SETTINGS_PERMISSION = 5;
 | 
			
		||||
 | 
			
		||||
    private Toolbar toolbar;
 | 
			
		||||
    private InnerView currentView;
 | 
			
		||||
@ -118,6 +121,7 @@ public class SwapWorkflowActivity extends AppCompatActivity {
 | 
			
		||||
    private PrepareSwapRepo updateSwappableAppsTask;
 | 
			
		||||
    private NewRepoConfig confirmSwapConfig;
 | 
			
		||||
    private LocalBroadcastManager localBroadcastManager;
 | 
			
		||||
    private WifiManager wifiManager;
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    private final ServiceConnection serviceConnection = new ServiceConnection() {
 | 
			
		||||
@ -183,6 +187,7 @@ public class SwapWorkflowActivity extends AppCompatActivity {
 | 
			
		||||
        container = (ViewGroup) findViewById(R.id.fragment_container);
 | 
			
		||||
 | 
			
		||||
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
 | 
			
		||||
        wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
 | 
			
		||||
 | 
			
		||||
        new SwapDebug().logStatus();
 | 
			
		||||
    }
 | 
			
		||||
@ -209,8 +214,20 @@ public class SwapWorkflowActivity extends AppCompatActivity {
 | 
			
		||||
        showRelevantView();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check whether incoming {@link Intent} is a swap repo, and ensure that
 | 
			
		||||
     * it is a valid swap URL.  The hostname can only be either an IP or
 | 
			
		||||
     * Bluetooth address.
 | 
			
		||||
     */
 | 
			
		||||
    private void checkIncomingIntent() {
 | 
			
		||||
        Intent intent = getIntent();
 | 
			
		||||
        Uri uri = intent.getData();
 | 
			
		||||
        if (uri != null && !HttpDownloader.isSwapUrl(uri) && !BluetoothDownloader.isBluetoothUri(uri)) {
 | 
			
		||||
            String msg = getString(R.string.swap_toast_invalid_url, uri);
 | 
			
		||||
            Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (intent.getBooleanExtra(EXTRA_CONFIRM, false) && !intent.getBooleanExtra(EXTRA_SWAP_INTENT_HANDLED, false)) {
 | 
			
		||||
            // Storing config in this variable will ensure that when showRelevantView() is next
 | 
			
		||||
            // run, it will show the connect swap view (if the service is available).
 | 
			
		||||
@ -239,32 +256,40 @@ public class SwapWorkflowActivity extends AppCompatActivity {
 | 
			
		||||
                    public void onClick(DialogInterface dialog, int which) {
 | 
			
		||||
                        // Do nothing
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                ).setPositiveButton(R.string.wifi, new DialogInterface.OnClickListener() {
 | 
			
		||||
                })
 | 
			
		||||
                .setPositiveButton(R.string.wifi, new DialogInterface.OnClickListener() {
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public void onClick(DialogInterface dialog, int which) {
 | 
			
		||||
                        startActivity(new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK));
 | 
			
		||||
                        SwapService.putWifiEnabledBeforeSwap(wifiManager.isWifiEnabled());
 | 
			
		||||
                        wifiManager.setWifiEnabled(true);
 | 
			
		||||
                        Intent intent = new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK);
 | 
			
		||||
                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
                        startActivity(intent);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                ).setNegativeButton(R.string.wifi_ap, new DialogInterface.OnClickListener() {
 | 
			
		||||
                })
 | 
			
		||||
                .setNegativeButton(R.string.wifi_ap, new DialogInterface.OnClickListener() {
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public void onClick(DialogInterface dialog, int which) {
 | 
			
		||||
                        promptToSetupWifiAP();
 | 
			
		||||
                        if (Build.VERSION.SDK_INT >= 26) {
 | 
			
		||||
                            showTetheringSettings();
 | 
			
		||||
                        } else if (Build.VERSION.SDK_INT >= 23 && !Settings.System.canWrite(getBaseContext())) {
 | 
			
		||||
                            requestWriteSettingsPermission();
 | 
			
		||||
                        } else {
 | 
			
		||||
                            setupWifiAP();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
        ).create().show();
 | 
			
		||||
                })
 | 
			
		||||
                .create().show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void promptToSetupWifiAP() {
 | 
			
		||||
        WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
 | 
			
		||||
    private void setupWifiAP() {
 | 
			
		||||
        WifiApControl ap = WifiApControl.getInstance(this);
 | 
			
		||||
        wifiManager.setWifiEnabled(false);
 | 
			
		||||
        if (!ap.enable()) {
 | 
			
		||||
            Log.e(TAG, "Could not enable WiFi AP.");
 | 
			
		||||
            // TODO: Feedback to user?
 | 
			
		||||
        if (ap.enable()) {
 | 
			
		||||
            Toast.makeText(this, R.string.swap_toast_hotspot_enabled, Toast.LENGTH_SHORT).show();
 | 
			
		||||
        } else {
 | 
			
		||||
            Utils.debugLog(TAG, "WiFi AP enabled.");
 | 
			
		||||
            // TODO: Seems to be broken some times...
 | 
			
		||||
            Toast.makeText(this, R.string.swap_toast_could_not_enable_hotspot, Toast.LENGTH_LONG).show();
 | 
			
		||||
            Log.e(TAG, "Could not enable WiFi AP.");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -408,29 +433,52 @@ public class SwapWorkflowActivity extends AppCompatActivity {
 | 
			
		||||
        inflateInnerView(R.layout.swap_select_apps);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * On {@code android-26}, only apps with privileges can access
 | 
			
		||||
     * {@code WRITE_SETTINGS}.  So this just shows the tethering settings
 | 
			
		||||
     * for the user to do it themselves.
 | 
			
		||||
     */
 | 
			
		||||
    public void showTetheringSettings() {
 | 
			
		||||
        final Intent intent = new Intent(Intent.ACTION_MAIN, null);
 | 
			
		||||
        intent.addCategory(Intent.CATEGORY_LAUNCHER);
 | 
			
		||||
        final ComponentName cn = new ComponentName("com.android.settings",
 | 
			
		||||
                "com.android.settings.TetherSettings");
 | 
			
		||||
        intent.setComponent(cn);
 | 
			
		||||
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
        startActivity(intent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @TargetApi(23)
 | 
			
		||||
    public void requestWriteSettingsPermission() {
 | 
			
		||||
        Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS,
 | 
			
		||||
                Uri.parse("package:" + getPackageName()));
 | 
			
		||||
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
        startActivityForResult(intent, REQUEST_WRITE_SETTINGS_PERMISSION);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void sendFDroid() {
 | 
			
		||||
        // If Bluetooth has not been enabled/turned on, then enabling device discoverability
 | 
			
		||||
        // will automatically enable Bluetooth.
 | 
			
		||||
        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
 | 
			
		||||
        if (adapter != null) {
 | 
			
		||||
            if (adapter.getState() != BluetoothAdapter.STATE_ON) {
 | 
			
		||||
        if (adapter == null
 | 
			
		||||
                || Build.VERSION.SDK_INT >= 23 // TODO make Bluetooth work with content:// URIs
 | 
			
		||||
                || (!adapter.isEnabled() && getService().getWifiSwap().isConnected())) {
 | 
			
		||||
            showSendFDroid();
 | 
			
		||||
        } else {
 | 
			
		||||
            sendFDroidBluetooth();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Send the F-Droid APK via Bluetooth.  If Bluetooth has not been
 | 
			
		||||
     * enabled/turned on, then enabling device discoverability will
 | 
			
		||||
     * automatically enable Bluetooth.
 | 
			
		||||
     */
 | 
			
		||||
    public void sendFDroidBluetooth() {
 | 
			
		||||
        if (BluetoothAdapter.getDefaultAdapter().isEnabled()) {
 | 
			
		||||
            sendFDroidApk();
 | 
			
		||||
        } else {
 | 
			
		||||
            Intent discoverBt = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
 | 
			
		||||
            discoverBt.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);
 | 
			
		||||
            startActivityForResult(discoverBt, REQUEST_BLUETOOTH_ENABLE_FOR_SEND);
 | 
			
		||||
            } else {
 | 
			
		||||
                sendFDroidApk();
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            new AlertDialog.Builder(this)
 | 
			
		||||
                    .setTitle(R.string.bluetooth_unavailable)
 | 
			
		||||
                    .setMessage(R.string.swap_cant_send_no_bluetooth)
 | 
			
		||||
                    .setNegativeButton(
 | 
			
		||||
                            R.string.cancel,
 | 
			
		||||
                            new DialogInterface.OnClickListener() {
 | 
			
		||||
                                @Override
 | 
			
		||||
                                public void onClick(DialogInterface dialog, int which) { }
 | 
			
		||||
                            }
 | 
			
		||||
                    ).create().show();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -483,6 +531,10 @@ public class SwapWorkflowActivity extends AppCompatActivity {
 | 
			
		||||
        inflateInnerView(R.layout.swap_wifi_qr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void showSendFDroid() {
 | 
			
		||||
        inflateInnerView(R.layout.swap_send_fdroid);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void showSwapConnected() {
 | 
			
		||||
        inflateInnerView(R.layout.swap_success);
 | 
			
		||||
    }
 | 
			
		||||
@ -557,23 +609,28 @@ public class SwapWorkflowActivity extends AppCompatActivity {
 | 
			
		||||
            }
 | 
			
		||||
        } else if (requestCode == CONNECT_TO_SWAP && resultCode == Activity.RESULT_OK) {
 | 
			
		||||
            finish();
 | 
			
		||||
        } else if (requestCode == REQUEST_WRITE_SETTINGS_PERMISSION) {
 | 
			
		||||
            if (Build.VERSION.SDK_INT >= 23 && Settings.System.canWrite(this)) {
 | 
			
		||||
                setupWifiAP();
 | 
			
		||||
            }
 | 
			
		||||
        } else if (requestCode == REQUEST_BLUETOOTH_ENABLE_FOR_SWAP) {
 | 
			
		||||
 | 
			
		||||
            if (resultCode == RESULT_OK) {
 | 
			
		||||
                Utils.debugLog(TAG, "User enabled Bluetooth, will make sure we are discoverable.");
 | 
			
		||||
                ensureBluetoothDiscoverableThenStart();
 | 
			
		||||
            } else {
 | 
			
		||||
                // Didn't enable bluetooth
 | 
			
		||||
                Utils.debugLog(TAG, "User chose not to enable Bluetooth, so doing nothing (i.e. sticking with wifi).");
 | 
			
		||||
                Utils.debugLog(TAG, "User chose not to enable Bluetooth, so doing nothing");
 | 
			
		||||
                SwapService.putBluetoothVisibleUserPreference(false);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } else if (requestCode == REQUEST_BLUETOOTH_DISCOVERABLE) {
 | 
			
		||||
 | 
			
		||||
            if (resultCode != RESULT_CANCELED) {
 | 
			
		||||
                Utils.debugLog(TAG, "User made Bluetooth discoverable, will proceed to start bluetooth server.");
 | 
			
		||||
                getState().getBluetoothSwap().startInBackground();
 | 
			
		||||
                getState().getBluetoothSwap().startInBackground(); // TODO replace with Intent to SwapService
 | 
			
		||||
            } else {
 | 
			
		||||
                Utils.debugLog(TAG, "User chose not to make Bluetooth discoverable, so doing nothing (i.e. sticking with wifi).");
 | 
			
		||||
                Utils.debugLog(TAG, "User chose not to make Bluetooth discoverable, so doing nothing");
 | 
			
		||||
                SwapService.putBluetoothVisibleUserPreference(false);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } else if (requestCode == REQUEST_BLUETOOTH_ENABLE_FOR_SEND) {
 | 
			
		||||
@ -583,12 +640,13 @@ public class SwapWorkflowActivity extends AppCompatActivity {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The process for setting up bluetooth is as follows:
 | 
			
		||||
     *  * Assume we have bluetooth available (otherwise the button which allowed us to start
 | 
			
		||||
     *    the bluetooth process should not have been available).
 | 
			
		||||
     *  * Ask user to enable (if not enabled yet).
 | 
			
		||||
     *  * Start bluetooth server socket.
 | 
			
		||||
     *  * Enable bluetooth discoverability, so that people can connect to our server socket.
 | 
			
		||||
     *
 | 
			
		||||
     * <ul>
 | 
			
		||||
     * <li>Assume we have bluetooth available (otherwise the button which allowed us to start
 | 
			
		||||
     * the bluetooth process should not have been available)</li>
 | 
			
		||||
     * <li>Ask user to enable (if not enabled yet)</li>
 | 
			
		||||
     * <li>Start bluetooth server socket</li>
 | 
			
		||||
     * <li>Enable bluetooth discoverability, so that people can connect to our server socket.</li>
 | 
			
		||||
     * </ul>
 | 
			
		||||
     * Note that this is a little different than the usual process for bluetooth _clients_, which
 | 
			
		||||
     * involves pairing and connecting with other devices.
 | 
			
		||||
     */
 | 
			
		||||
@ -629,12 +687,12 @@ public class SwapWorkflowActivity extends AppCompatActivity {
 | 
			
		||||
            throw new IllegalStateException("Can't start Bluetooth swap because service is null for some strange reason.");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        service.getBluetoothSwap().startInBackground();
 | 
			
		||||
        service.getBluetoothSwap().startInBackground();  // TODO replace with Intent to SwapService
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class PrepareInitialSwapRepo extends PrepareSwapRepo {
 | 
			
		||||
        PrepareInitialSwapRepo() {
 | 
			
		||||
            super(new HashSet<>(Arrays.asList(new String[] {BuildConfig.APPLICATION_ID})));
 | 
			
		||||
            super(new HashSet<>(Arrays.asList(new String[]{BuildConfig.APPLICATION_ID})));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,7 @@ import android.content.Intent;
 | 
			
		||||
import android.content.IntentFilter;
 | 
			
		||||
import android.graphics.LightingColorFilter;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.support.annotation.ColorRes;
 | 
			
		||||
import android.support.annotation.NonNull;
 | 
			
		||||
import android.support.v4.content.LocalBroadcastManager;
 | 
			
		||||
@ -19,9 +20,6 @@ import android.widget.Button;
 | 
			
		||||
import android.widget.ImageView;
 | 
			
		||||
import android.widget.ScrollView;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
 | 
			
		||||
import org.apache.http.NameValuePair;
 | 
			
		||||
import org.apache.http.client.utils.URLEncodedUtils;
 | 
			
		||||
import org.fdroid.fdroid.FDroidApp;
 | 
			
		||||
import org.fdroid.fdroid.Preferences;
 | 
			
		||||
import org.fdroid.fdroid.QrGenAsyncTask;
 | 
			
		||||
@ -31,9 +29,8 @@ import org.fdroid.fdroid.localrepo.SwapService;
 | 
			
		||||
import org.fdroid.fdroid.net.WifiStateChangeService;
 | 
			
		||||
import org.fdroid.fdroid.views.swap.device.camera.CameraCharacteristicsChecker;
 | 
			
		||||
 | 
			
		||||
import java.net.URI;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Locale;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
public class WifiQrView extends ScrollView implements SwapWorkflowActivity.InnerView {
 | 
			
		||||
 | 
			
		||||
@ -142,32 +139,34 @@ public class WifiQrView extends ScrollView implements SwapWorkflowActivity.Inner
 | 
			
		||||
        ipAddressView.setText(buttonLabel);
 | 
			
		||||
 | 
			
		||||
        Uri sharingUri = Utils.getSharingUri(FDroidApp.repo);
 | 
			
		||||
        String qrUriString = scheme + sharingUri.getHost();
 | 
			
		||||
        StringBuilder qrUrlBuilder = new StringBuilder(scheme);
 | 
			
		||||
        qrUrlBuilder.append(sharingUri.getHost());
 | 
			
		||||
        if (sharingUri.getPort() != 80) {
 | 
			
		||||
            qrUriString += ":" + sharingUri.getPort();
 | 
			
		||||
            qrUrlBuilder.append(':');
 | 
			
		||||
            qrUrlBuilder.append(sharingUri.getPort());
 | 
			
		||||
        }
 | 
			
		||||
        qrUriString += sharingUri.getPath();
 | 
			
		||||
        qrUrlBuilder.append(sharingUri.getPath());
 | 
			
		||||
        boolean first = true;
 | 
			
		||||
 | 
			
		||||
        // Andorid provides an API for getting the query parameters and iterating over them:
 | 
			
		||||
        //   Uri.getQueryParameterNames()
 | 
			
		||||
        // But it is only available on later Android versions. As such we use URLEncodedUtils instead.
 | 
			
		||||
        List<NameValuePair> parameters = URLEncodedUtils.parse(URI.create(sharingUri.toString()), "UTF-8");
 | 
			
		||||
        for (NameValuePair parameter : parameters) {
 | 
			
		||||
            if (!"ssid".equals(parameter.getName())) {
 | 
			
		||||
        if (Build.VERSION.SDK_INT > 10) {
 | 
			
		||||
            Set<String> names = sharingUri.getQueryParameterNames();
 | 
			
		||||
            for (String name : names) {
 | 
			
		||||
                if (!"ssid".equals(name)) {
 | 
			
		||||
                    if (first) {
 | 
			
		||||
                    qrUriString += "?";
 | 
			
		||||
                        qrUrlBuilder.append('?');
 | 
			
		||||
                        first = false;
 | 
			
		||||
                    } else {
 | 
			
		||||
                    qrUriString += "&";
 | 
			
		||||
                        qrUrlBuilder.append('&');
 | 
			
		||||
                    }
 | 
			
		||||
                    qrUrlBuilder.append(name.toUpperCase(Locale.ENGLISH));
 | 
			
		||||
                    qrUrlBuilder.append('=');
 | 
			
		||||
                    qrUrlBuilder.append(sharingUri.getQueryParameter(name).toUpperCase(Locale.ENGLISH));
 | 
			
		||||
                }
 | 
			
		||||
                qrUriString += parameter.getName().toUpperCase(Locale.ENGLISH) + "=" +
 | 
			
		||||
                        parameter.getValue().toUpperCase(Locale.ENGLISH);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        String qrUriString = qrUrlBuilder.toString();
 | 
			
		||||
        Utils.debugLog(TAG, "Encoded swap URI in QR Code: " + qrUriString);
 | 
			
		||||
 | 
			
		||||
        new QrGenAsyncTask(getActivity(), R.id.wifi_qr_code).execute(qrUriString);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -58,7 +58,7 @@
 | 
			
		||||
        <android.support.v7.widget.SwitchCompat
 | 
			
		||||
            android:layout_width="wrap_content"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            tools:checked="true"
 | 
			
		||||
            android:enabled="false"
 | 
			
		||||
            android:id="@+id/switch_bluetooth" />
 | 
			
		||||
 | 
			
		||||
    </LinearLayout>
 | 
			
		||||
@ -114,7 +114,7 @@
 | 
			
		||||
        <android.support.v7.widget.SwitchCompat
 | 
			
		||||
            android:layout_width="wrap_content"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            tools:checked="false"
 | 
			
		||||
            android:enabled="false"
 | 
			
		||||
            android:id="@+id/switch_wifi" />
 | 
			
		||||
 | 
			
		||||
    </LinearLayout>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										50
									
								
								app/src/main/res/layout/swap_send_fdroid.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								app/src/main/res/layout/swap_send_fdroid.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
 | 
			
		||||
<org.fdroid.fdroid.views.swap.SendFDroidView
 | 
			
		||||
        xmlns:tools="http://schemas.android.com/tools"
 | 
			
		||||
        xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
        android:background="@color/swap_blue"
 | 
			
		||||
        android:layout_height="match_parent"
 | 
			
		||||
        android:layout_width="wrap_content">
 | 
			
		||||
 | 
			
		||||
    <LinearLayout android:orientation="vertical"
 | 
			
		||||
                  android:gravity="center"
 | 
			
		||||
                  android:layout_width="wrap_content"
 | 
			
		||||
                  android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
        <ImageView
 | 
			
		||||
                android:layout_width="250dp"
 | 
			
		||||
                android:layout_height="250dp"
 | 
			
		||||
                android:maxHeight="20dp"
 | 
			
		||||
                android:id="@+id/wifi_qr_code"
 | 
			
		||||
                tools:src="@drawable/swap_qr_example"/>
 | 
			
		||||
 | 
			
		||||
        <TextView
 | 
			
		||||
                android:layout_width="wrap_content"
 | 
			
		||||
                android:layout_height="wrap_content"
 | 
			
		||||
                android:text="@string/swap_scan_or_type_url"
 | 
			
		||||
                style="@style/SwapTheme.Wizard.MainText"/>
 | 
			
		||||
 | 
			
		||||
        <TextView
 | 
			
		||||
                android:layout_width="wrap_content"
 | 
			
		||||
                android:layout_height="wrap_content"
 | 
			
		||||
                android:id="@+id/device_ip_address"
 | 
			
		||||
                tools:text="http://255.255.255.255:8888"
 | 
			
		||||
                style="@style/SwapTheme.Wizard.LocalIpAddress"/>
 | 
			
		||||
 | 
			
		||||
        <Button style="@style/SwapTheme.Wizard.OptionButton"
 | 
			
		||||
                android:text="@string/use_bluetooth"
 | 
			
		||||
                android:layout_gravity="center"
 | 
			
		||||
                android:id="@+id/btn_use_bluetooth"/>
 | 
			
		||||
 | 
			
		||||
        <TextView
 | 
			
		||||
                android:id="@+id/warning_qr_scanner"
 | 
			
		||||
                android:layout_width="wrap_content"
 | 
			
		||||
                android:layout_height="wrap_content"
 | 
			
		||||
                android:text="@string/warning_scaning_qr_code"
 | 
			
		||||
                android:visibility="gone"
 | 
			
		||||
                style="@style/SwapTheme.Wizard.QRScanWarningText"/>
 | 
			
		||||
 | 
			
		||||
    </LinearLayout>
 | 
			
		||||
 | 
			
		||||
</org.fdroid.fdroid.views.swap.SendFDroidView>
 | 
			
		||||
@ -333,10 +333,6 @@
 | 
			
		||||
    <string name="swap_intro">Verbind en deel programme met mense naby jou.</string>
 | 
			
		||||
    <string name="swap_confirm">Bevestig uitruil</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">Die QR kode jy geskandeer het lyk nie soos \'n uitruil kode nie.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth nie beskikbaar nie</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">"Kan nie F-Droid stuur nie, omdat Bluetooth is nie beskikbaar is op
 | 
			
		||||
        hierdie toestel nie."
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="loading">Laai tans…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">\'n Fout het plaasgevind tydens koppeling met die toestel, dit lyk nie asof ons kan uitruil nie!</string>
 | 
			
		||||
    <string name="swap_not_enabled">Uitruil nie geaktiveer nie</string>
 | 
			
		||||
 | 
			
		||||
@ -287,8 +287,6 @@
 | 
			
		||||
    <string name="swap_connecting">جاري الاتصال</string>
 | 
			
		||||
    <string name="swap_confirm">تأكيد المبادلة</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">رمز الاستجابة السريعة الذي تم التقاطه لا يبدو وكأنه رمز المبادلة.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">البلوتوث غير متاح</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">لا يمكن إرسال اف-درويد، بسبب أن البلوتوث غير متاح على هذا الجهاز.</string>
 | 
			
		||||
    <string name="loading">يتم التحميل…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">حدث خطأ أثناء الاتصال بالجهاز، ولا يمكن أن يجري المبادلة معه!</string>
 | 
			
		||||
    <string name="swap_not_enabled">المبادلة غير فعالة</string>
 | 
			
		||||
 | 
			
		||||
@ -176,15 +176,11 @@
 | 
			
		||||
    <string name="uninstall_update_confirm">¿Quies trocar esta aplicación pola versión de fábrica?</string>
 | 
			
		||||
    <string name="perm_costs_money">Quiciabes esto te cueste perres</string>
 | 
			
		||||
    <string name="loading">Cargando…</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Nun pue unviase F-droid porque\'l Bluetooth nun ta disponible nesti
 | 
			
		||||
        preséu.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="swap_connecting">Coneutando</string>
 | 
			
		||||
    <string name="swap_cant_find_peers">¿Nun pues alcontrar a quien tas guetando?</string>
 | 
			
		||||
    <string name="swap_send_fdroid">Unviar F-droid</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">El códigu QR qu\'escaniesti nun paez ser un códigu d\'intercambéu.</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Asocedió un fallu entrín se coneutaba col preséu, ¡nun podemos facer l\'intercambiu con elli!</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth non disponible</string>
 | 
			
		||||
    <string name="swap_confirm">Confirmar intercambéu</string>
 | 
			
		||||
    <string name="swap_wifi_device_name">Nome del preséu</string>
 | 
			
		||||
    <string name="swap_visible_bluetooth">Visible pente Bluetooth</string>
 | 
			
		||||
 | 
			
		||||
@ -324,10 +324,6 @@
 | 
			
		||||
    <string name="swap_connecting">Злучэнне</string>
 | 
			
		||||
    <string name="swap_confirm">Пацвердзіць абмен</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">QR-код, які вы адсканавалі, не выглядае як код абмену.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth не даступны</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Немагчыма адправіць F-Droid, бо Bluetooth не даступны на гэтай
 | 
			
		||||
        прыладзе.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="loading">Загрузка…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Здарылася памылка падчас злучэння з прыладай. Немагчыма прадоўжыць абмен!</string>
 | 
			
		||||
    <string name="swap_not_enabled">Абмен не ўключаны</string>
 | 
			
		||||
 | 
			
		||||
@ -220,10 +220,6 @@
 | 
			
		||||
    </string>
 | 
			
		||||
 | 
			
		||||
    <string name="swap_not_enabled">Забранена размяна</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Изпращането на F-Droid не е възможно защото липсва Bluetooth
 | 
			
		||||
        функционалност.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Липсва Bluetooth</string>
 | 
			
		||||
    <string name="swap_nearby">Размяна с устройства наоколо</string>
 | 
			
		||||
    <string name="local_repo_running">F-Droid e в готовност за размяна</string>
 | 
			
		||||
    <string name="touch_to_configure_local_repo">Натиснете за детайли или да позволите размяна на приложение.</string>
 | 
			
		||||
 | 
			
		||||
@ -314,8 +314,6 @@
 | 
			
		||||
    <string name="swap_connecting">མཐུད་བཞིན་པ།</string>
 | 
			
		||||
    <string name="swap_confirm">བརྗེ་ལེན་གཏན་འཁེལ།</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">ཁྱེད་རང་གིས་འཚག་རྒྱག་པའི་QR ཨང་རྟགས་འདི་བརྗེ་ལེན་ཨང་རྟགས་དང་འདྲ་མཚུངས་མིན་འདུག</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">བྷུ་ལུ་ཊོཐ་མིན་འདུག</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">ཨེཕ་རོཌ་གཏོང་ཐུབ་ཀྱི་མིན་འདུག གང་ཡིན་ཟེར་ན་ཡོ་བྱད་འདིའི་སྒང་ལ་བྷུ་ལུ་ཊོཐ་མིན་འདུག</string>
 | 
			
		||||
    <string name="loading">བཅུག་བཞིན་པ།..…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">ཡོ་བྱད་ལ་མཐུད་པའི་སྐབས་སུ་སྐྱོན་ཤོར་སོང་བས་དེ་དང་མཉམ་དུ་བརྗེ་ལེན་བྱེད་ཐུབ་ཀྱི་མིན་འདུག!</string>
 | 
			
		||||
    <string name="swap_not_enabled">བརྗེ་ལེན་སྒོ་ཕྱེས་མིན་འདུག</string>
 | 
			
		||||
 | 
			
		||||
@ -273,8 +273,6 @@
 | 
			
		||||
    <string name="swap_connecting">Connectant</string>
 | 
			
		||||
    <string name="swap_confirm">Confirmeu l\'intercanvi</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">El codi QR que heu escanejat no sembla un codi d\'intercanvi.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">El Bluetooth no està disponible</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">No s\'ha pogut enviar l\'F-Droid perquè el Bluetooth no està disponible en aquest dispositiu.</string>
 | 
			
		||||
    <string name="loading">S\'està carregant…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">S\'ha produït un error en connectar-se al dispositiu. No us podeu connectar!</string>
 | 
			
		||||
    <string name="swap_not_enabled">Intercanvi no activat</string>
 | 
			
		||||
 | 
			
		||||
@ -242,10 +242,6 @@
 | 
			
		||||
    <string name="swap_connecting">Připojování</string>
 | 
			
		||||
    <string name="swap_confirm">Potvrdit výměnu</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">Naskenovaný QR kód nevypadá jako výměnný kód.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth nedostupné</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">F-Droid nelze odeslat, protože na tomto přístroji není Bluetooth
 | 
			
		||||
        dostupné.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="loading">Načítání…</string>
 | 
			
		||||
    <string name="swap_not_enabled">Výměna není povolena</string>
 | 
			
		||||
    <string name="swap_not_enabled_description">Před výměnou je třeba zviditelnit přístroj.</string>
 | 
			
		||||
 | 
			
		||||
@ -279,9 +279,6 @@
 | 
			
		||||
    <string name="swap_connecting">Forbinder</string>
 | 
			
		||||
    <string name="swap_confirm">Bekræft udveksling</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">Den QR-kode du skannede ligner ikke en udvekslingskode.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth utilgængelig</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Kan ikke sende F-Droid fordi Bluetooth er utilgængeligt på denne enhed.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="loading">Indlæser…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Der opstod en fejl under forbindelsen til enheden, kan udveksle med den!</string>
 | 
			
		||||
    <string name="swap_not_enabled">Udveksling ikke aktiveret</string>
 | 
			
		||||
 | 
			
		||||
@ -192,7 +192,6 @@
 | 
			
		||||
 | 
			
		||||
    <string name="system_uninstall_button">Deinstallieren</string>
 | 
			
		||||
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth nicht verfügbar</string>
 | 
			
		||||
    <string name="newPerms">Neu</string>
 | 
			
		||||
    <string name="perms_new_perm_prefix">Neu:</string>
 | 
			
		||||
    <string name="interval_1h">Stündlich</string>
 | 
			
		||||
@ -276,9 +275,6 @@
 | 
			
		||||
        Sie keinen gemeinsamen Netzwerkzugang haben, kann einer von Ihnen einen WLAN-Hotspot einrichten.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="swap_nearby">Tausch in der Nähe</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">F-Droid kann nicht gesendet werden, da Bluetooth auf diesem Gerät nicht
 | 
			
		||||
        verfügbar ist.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="loading">Ladevorgang …</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Fehler bei der Verbindungsaufnahme zum Zielgerät. Tausch ist nicht möglich!</string>
 | 
			
		||||
    <string name="swap_not_enabled">Tauschen ist nicht aktiviert</string>
 | 
			
		||||
 | 
			
		||||
@ -267,10 +267,6 @@
 | 
			
		||||
    <string name="swap_connecting">Σύνδεση</string>
 | 
			
		||||
    <string name="swap_confirm">Επιβεβαίωση ανταλλαγής</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">Ο κώδικας QR που σαρώσατε δεν μοιάζει με έναν κωδικό ανταλλαγής.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Το Bluetooth δεν είναι διαθέσιμο</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Δεν μπορείτε να στείλετε το F-Droid επειδή το Bluetooth δεν είναι
 | 
			
		||||
        διαθέσιμο σε αυτήν τη συσκευή.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="loading">Φόρτωση…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Παρουσιάστηκε σφάλμα κατά τη σύνδεση με την συσκευή, φαίνεται ότι δεν μπορούμε να ανταλλάξουμε με αυτή!</string>
 | 
			
		||||
    <string name="swap_not_enabled">Η ανταλλαγή δεν είναι ενεργοποιημένη</string>
 | 
			
		||||
 | 
			
		||||
@ -272,10 +272,6 @@
 | 
			
		||||
    <string name="swap_connecting">Konektado</string>
 | 
			
		||||
    <string name="swap_confirm">Konfirmi interŝanĝon</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">La skanita QR-kodo ne estas interŝanĝa kodo.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bludento ne disponebla</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Ne povas sendi F-Droid, ĉar Bludento ne estas disponebla en via
 | 
			
		||||
        aparato.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="loading">Prilaborado…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Eraro okazis dum konektado al aparato, ne povas interŝanĝi kun ĝi!</string>
 | 
			
		||||
    <string name="swap_not_enabled">Interŝanĝo malaktiva</string>
 | 
			
		||||
 | 
			
		||||
@ -236,8 +236,6 @@
 | 
			
		||||
    <string name="swap_connecting">Conectando</string>
 | 
			
		||||
    <string name="swap_confirm">Confirmar intercambio</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">El código QR escaneado no parece un código de intercambio.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth no disponible</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">No se puede enviar F-Droid porque no está disponible Bluetooth en este dispositivo.</string>
 | 
			
		||||
    <string name="loading">Cargando…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Ocurrió un error mientras se conectaba al dispositivo. ¡No podemos intercambiar con él!</string>
 | 
			
		||||
    <string name="swap_not_enabled">Intercambio no activado</string>
 | 
			
		||||
 | 
			
		||||
@ -302,9 +302,6 @@
 | 
			
		||||
    <string name="swap_connecting">Ühenduse loomine</string>
 | 
			
		||||
    <string name="swap_confirm">Kinnita vahetus</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">Skannitud QR-kood ei paista olevat vahetamise kood.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth ei ole saadaval</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">F-Droidi ei saa saata, sest Bluetooth ei ole selles seadmes saadaval.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="loading">Laadimine…</string>
 | 
			
		||||
    <string name="swap_not_enabled">Vahetamine ei ole lubatud</string>
 | 
			
		||||
    <string name="swap_not_enabled_description">Enne vahetamist pead tegema seadme nähtavaks.</string>
 | 
			
		||||
 | 
			
		||||
@ -293,10 +293,6 @@
 | 
			
		||||
        sare berera konektatzerik zuetako batek Wi-Fi gune bat sor dezake.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">Eskaneatu duzun QR kodea ez diruri truke kode bat.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth ez dago eskuragarri</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Ezin da F-Droid bidali, Bluetooth ez dagoelako erabilgarri gailu
 | 
			
		||||
        honetan.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Errore bat gertatu da gailura konektatzean, ezin dugu honekin trukatu!</string>
 | 
			
		||||
    <string name="swap_not_enabled">Trukea ez dago gaituta</string>
 | 
			
		||||
    <string name="swap_not_enabled_description">Trukatu aurretik, gailua ikusgai jarri behar duzu.</string>
 | 
			
		||||
 | 
			
		||||
@ -274,8 +274,6 @@
 | 
			
		||||
    <string name="swap_nfc_description">اگر دوستتان افدروید دارد و NFC اش روشن است، دستگاه هایتان را با هم تماس بدهید.</string>
 | 
			
		||||
    <string name="swap_join_same_wifi_desc">برای تبادل با استفاده از وایفای، مطمئن شوید که روی شبکهٔ یکسانی هستید. اگر به شبکهٔ یکسان دسترسی ندارید، یکی از شما میتواند یک هاتسپات وایفای ایجاد کند.</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">رمز QR ای که اسکن کردید، شبیه یک رمز تبادل نیست.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">بلوتوث موجود نیست</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">نمیتوان افدروید را فرستاد، زیرا بلوتوث روی این دستگاه موجود نیست.</string>
 | 
			
		||||
    <string name="loading">در حال بارگزاری…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">هنگام اتّصال به دستگاه خطایی رخ داد، نمیتوان تبادل کرد!</string>
 | 
			
		||||
    <string name="swap_not_enabled">تبادل فعّال نیست</string>
 | 
			
		||||
 | 
			
		||||
@ -313,10 +313,6 @@
 | 
			
		||||
    <string name="swap_connecting">Yhdistetään</string>
 | 
			
		||||
    <string name="swap_confirm">Vahvista vaihto</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">Skannaamasi QR-koodi ei näytä vaihtamiseen tarkoitetulta koodilta.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth ei ole saatavilla</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Ei voida lähettää F-Droidia, koska Bluetooth ei ole saatavilla tällä
 | 
			
		||||
        laitteella.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Tapahtui virhe yhdistettäessä laitteeseen, vaihtaminen ei onnistu.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="swap_not_enabled">Vaihtaminen ei ole käytössä</string>
 | 
			
		||||
 | 
			
		||||
@ -260,10 +260,6 @@
 | 
			
		||||
    <string name="swap_connecting">Connexion</string>
 | 
			
		||||
    <string name="swap_confirm">Confirmez l\'échange</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">Le code QR que vous avez scanné ne ressemble pas à un code d\'échange.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth indisponible</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Impossible d\'envoyer F-Droid : le Bluetooth n\'est pas disponible sur
 | 
			
		||||
        cet appareil.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="swap_not_enabled_description">Votre appareil doit être visible avant de pouvoir commencer l\'échange.</string>
 | 
			
		||||
 | 
			
		||||
    <string name="interval_1h">Toutes les heures</string>
 | 
			
		||||
 | 
			
		||||
@ -191,7 +191,6 @@
 | 
			
		||||
    <string name="swap_send_fdroid">Enviar F-Droid</string>
 | 
			
		||||
    <string name="swap_connecting">Conectando</string>
 | 
			
		||||
    <string name="swap_confirm">Confirma o intercambio</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">O Bluetooth non está dispoñible</string>
 | 
			
		||||
    <string name="loading">Cargando…</string>
 | 
			
		||||
    <string name="newPerms">Novo</string>
 | 
			
		||||
    <string name="allPerms">Todo</string>
 | 
			
		||||
@ -290,9 +289,6 @@
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">O código QR que escaneaches non parece corresponder a un código de
 | 
			
		||||
        intercambio.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Non se pode enviar, porque o Bluetooth non está dispoñible neste
 | 
			
		||||
        dispositivo.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Ocurriu un erro durante a conexión co dispositivo, non se pode realizar o intercambio!</string>
 | 
			
		||||
    <string name="swap_not_enabled">Intercambio non dispoñible</string>
 | 
			
		||||
    <string name="swap_not_enabled_description">Antes do intercambio, o teu dispositivo debe estar visible.</string>
 | 
			
		||||
 | 
			
		||||
@ -227,7 +227,6 @@
 | 
			
		||||
    <string name="swap_not_visible_wifi">לא גלוי דרך רשת אלחוטית</string>
 | 
			
		||||
    <string name="swap_wifi_device_name">שם התקן</string>
 | 
			
		||||
    <string name="swap_connecting">מתבצע חיבור</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth לא זמין</string>
 | 
			
		||||
    <string name="loading">בטעינה…</string>
 | 
			
		||||
 | 
			
		||||
    <string name="interval_never">אף פעם</string>
 | 
			
		||||
@ -249,7 +248,6 @@
 | 
			
		||||
    <string name="swap_send_fdroid">שליחת F-Droid</string>
 | 
			
		||||
    <string name="swap_confirm">אימות ההחלפה</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">קוד ה־QR שסרקת לא נראה כמו קוד החלפה.</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">אין אפשרות לשלוח את F-Droid כיוון שה־Bluetooth אינו זמין בהתקן זה.</string>
 | 
			
		||||
    <string name="swap_not_enabled">ההחלפה מנוטרלת</string>
 | 
			
		||||
    <string name="swap_not_enabled_description">בטרם ביצוע החלפה, על מכשירך להיות גלוי.</string>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -96,9 +96,6 @@
 | 
			
		||||
    <string name="loading">लोड हो रहा है…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">यन्त्र को जोड़ने मैं एक समस्या आ गयी| यन्त्र से अदला-बदली नहीं हो सकती|
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="bluetooth_unavailable">ब्लूटूथ अनुपलब्ध</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">फ-द्रोइड नहीं भेज सकते, क्योंकि ब्लूटूथ इस यन्त्र पे उपलब्ध नहीं है|
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="swap_connecting">जुड़ रहा है</string>
 | 
			
		||||
    <string name="swap_confirm">अदला-बदली की पुष्टि करे</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">QR कोड जो अपने स्कैन किया है वह अदला-बदली का code नहीं है|</string>
 | 
			
		||||
 | 
			
		||||
@ -315,9 +315,6 @@
 | 
			
		||||
    <string name="swap_connecting">Spajanje</string>
 | 
			
		||||
    <string name="swap_confirm">Potvrdi razmjenu</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">Čini se da QR kod koji ste skenirali nije kod za razmjenu.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth nedostupan</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Nije uspjelo slanje F-Droida, Bluetooth nije dostupan na ovom uređaju.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="loading">Učitavanje…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Došlo je do greške prilikom spajanja na uređaj, čini se da razmjena nije
 | 
			
		||||
        moguća.
 | 
			
		||||
 | 
			
		||||
@ -269,10 +269,6 @@
 | 
			
		||||
    <string name="swap_cant_find_peers">Nem találja, amit keres?</string>
 | 
			
		||||
    <string name="swap_send_fdroid">Az F-Droid küldése</string>
 | 
			
		||||
    <string name="swap_connecting">Kapcsolódás</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">A Bluetooth nem érhető el</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Az F-Droid küldése sikertelen, mivel a Bluetooth nem érhető el ezen az
 | 
			
		||||
        eszközön.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="download_pending">Várakozás a letöltés elkezdésére…</string>
 | 
			
		||||
    <string name="installing">Telepítés…</string>
 | 
			
		||||
    <string name="uninstalling">Eltávolítás…</string>
 | 
			
		||||
 | 
			
		||||
@ -294,10 +294,6 @@
 | 
			
		||||
    <string name="swap_connecting">Menyambung</string>
 | 
			
		||||
    <string name="swap_confirm">Konfirmasi swap</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">Kode QR yang kamu pindai tidak terlihat seperti kode swap.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth tidak tersedia</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Tidak dapat mengirim F-Droid, karena Bluetooth tidak tersedia diperangkat
 | 
			
		||||
        ini.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="loading">Memuat…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Galat terjadi ketika menyambungkan ke perangkat, tidak bisa melakukan pertukaran!</string>
 | 
			
		||||
    <string name="swap_not_enabled">Swap dinonaktifkan</string>
 | 
			
		||||
 | 
			
		||||
@ -220,7 +220,6 @@
 | 
			
		||||
    <string name="swap_no_peers_nearby">Gat ekki fundið neinn í nágrenninu til að býtta við.</string>
 | 
			
		||||
    <string name="swap_connecting">Tengist</string>
 | 
			
		||||
    <string name="swap_confirm">Staðfesta býtti</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth er ekki tiltækt</string>
 | 
			
		||||
    <string name="loading">Hleð inn…</string>
 | 
			
		||||
    <string name="swap_not_enabled">Forritabýtti eru óvirk</string>
 | 
			
		||||
    <string name="install_confirm">þarf aðgang að</string>
 | 
			
		||||
@ -369,8 +368,6 @@
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="swap_join_this_hotspot">Hjálpaðu vini þínum að tengjast tengipunktinum þínum</string>
 | 
			
		||||
    <string name="swap_scan_or_type_url">Einn aðili þarf að skanna QR-kóðann, eða slá slóð hins inn í vafra.</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Get ekki sent F-Droid, því Bluetooth er ekki til taks á þessu tæki.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Villa kom upp við að tengjast við tækið, ekki er hægt að nota það við býtti!</string>
 | 
			
		||||
    <string name="swap_not_enabled_description">Áður en farið er í að býtta þarf að gera tækið þitt sýnilegt.</string>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -200,7 +200,6 @@
 | 
			
		||||
        non saranno rimossi. L\'app dopo l\'aggiornamento avrà accesso a:
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="loading">Caricamento…</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth non disponibile</string>
 | 
			
		||||
    <string name="swap_connecting">Connessione in corso</string>
 | 
			
		||||
    <string name="swap_wifi_device_name">Nome del dispositivo</string>
 | 
			
		||||
    <string name="swap_cant_find_peers">Non riesci a trovare chi stai cercando?</string>
 | 
			
		||||
@ -320,9 +319,6 @@
 | 
			
		||||
        browser.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="swap_send_fdroid">Invia F-Droid</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Impossibile inviare F-Droid, perché il Bluetooth non è disponibile su
 | 
			
		||||
        questo dispositivo.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="swap_connection_misc_error">È avvenuto un errore durante la connessione al dispositivo, non sembra possibile effettuare lo scambio!</string>
 | 
			
		||||
    <string name="swap_not_enabled">Scambio non abilitato</string>
 | 
			
		||||
    <string name="install_confirm_update_no_perms">Vuoi installare una versione aggiornata
 | 
			
		||||
 | 
			
		||||
@ -210,8 +210,6 @@
 | 
			
		||||
    <string name="swap_connecting">接続中</string>
 | 
			
		||||
    <string name="swap_confirm">交換の確認</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">読み取ったQRコードは交換コードではないようです。</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetoothが利用できません</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">この端末ではBluetoothが利用できないため、F-Droidを送信できません。</string>
 | 
			
		||||
    <string name="loading">読み込み中</string>
 | 
			
		||||
 | 
			
		||||
    <string name="downloading">ダウンロード中</string>
 | 
			
		||||
 | 
			
		||||
@ -156,7 +156,6 @@
 | 
			
		||||
    <string name="swap_choose_apps">Fren Asnas</string>
 | 
			
		||||
    <string name="swap_wifi_device_name">Isem n yibenk</string>
 | 
			
		||||
    <string name="swap_confirm">Sentem ambaddal</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth yella</string>
 | 
			
		||||
    <string name="interval_never">Warǧin</string>
 | 
			
		||||
    <string name="keep_week">1 n umalas</string>
 | 
			
		||||
    <string name="keep_forever">Yal tikelt</string>
 | 
			
		||||
 | 
			
		||||
@ -249,8 +249,6 @@
 | 
			
		||||
    <string name="swap_connecting">연결 중</string>
 | 
			
		||||
    <string name="swap_confirm">교환 확인</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">스캔한 QR 코드는 교환 코드가 아닌 것으로 파악됩니다.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">블루투스를 사용할 수 없음</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">F-Droid를 보낼 수 없습니다. 이 장치에서 블루투스를 사용할 수 없기 때문입니다.</string>
 | 
			
		||||
    <string name="loading">불러오는 중…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">장치에 연결하는 동안 오류가 발생했습니다. 교환할 수 없습니다!</string>
 | 
			
		||||
    <string name="swap_not_enabled">교환이 활성화되어 있지 않음</string>
 | 
			
		||||
 | 
			
		||||
@ -328,8 +328,6 @@
 | 
			
		||||
    <string name="swap_connecting">ബന്ധിപ്പിക്കുന്നു</string>
 | 
			
		||||
    <string name="swap_confirm">കെെമാറ്റം സ്ഥിരീകരിക്കുക</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">നിങ്ങൾ സ്കാന് ചെയ്ത QR കോഡ് ഒരു സ്വാപ്പ് കോഡ് പോലെ തോന്നുന്നില്ല.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">ബ്ലൂടൂത്ത് ലഭ്യമല്ല</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">ബ്ലൂടൂത്ത് ഈ ഉപകരണത്തിൽ ലഭ്യമല്ലാത്തതിനാൽ എഫ്-ഡ്രോയ്ഡ് അയയ്ക്കാൻ കഴിയില്ല.</string>
 | 
			
		||||
    <string name="loading">ലോഡുചെയ്യുന്നു…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">ഉപകരണത്തിലേക്ക് കണക്റ്റുചെയ്യുമ്പോൾ പിശക് സംഭവിച്ചു, അതുമായി കെെമാറ്റം ചെയ്യാന് പറ്റുന്നില്ല!</string>
 | 
			
		||||
    <string name="swap_not_enabled">കെെമാറ്റം സജ്ജമല്ല</string>
 | 
			
		||||
 | 
			
		||||
@ -215,7 +215,6 @@
 | 
			
		||||
    <string name="swap_cant_find_peers">သင္ရွာေနတာကိုမေတြ႕ဘူးလား?</string>
 | 
			
		||||
    <string name="swap_send_fdroid">F-Droid ပို႔မည္</string>
 | 
			
		||||
    <string name="swap_connecting">ခ်ိတ္ဆက္ေနသည္</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">ဘလူးသုဒ္မရရွိႏိုင္ပါ</string>
 | 
			
		||||
    <string name="loading">ဖြင့္ေနသည္</string>
 | 
			
		||||
    <string name="newPerms">အသစ္</string>
 | 
			
		||||
    <string name="allPerms">အားလံုး</string>
 | 
			
		||||
 | 
			
		||||
@ -212,10 +212,6 @@
 | 
			
		||||
    <string name="swap_cant_find_peers">Finner du ikke den du leter etter?</string>
 | 
			
		||||
    <string name="swap_send_fdroid">Oversend F-Droid</string>
 | 
			
		||||
    <string name="swap_connecting">Kobler til</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Blåtann utilgjengelig</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Kan ikke sende F-Droid siden Blåtann ikke er tilgjengelig på denne
 | 
			
		||||
        enheten.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="loading">Laster inn…</string>
 | 
			
		||||
    <string name="install_confirm">tilgang til</string>
 | 
			
		||||
    <string name="newPerms">Ny</string>
 | 
			
		||||
 | 
			
		||||
@ -223,10 +223,6 @@
 | 
			
		||||
    <string name="swap_connecting">Verbinden</string>
 | 
			
		||||
    <string name="swap_confirm">Bevestig uitwisseling</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">De QR-code die je gescand hebt lijkt geen uitwisselcode te zijn.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth niet beschikbaar</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Kon F-Droid niet verzenden omdat Bluetooth niet beschikbaar is op dit
 | 
			
		||||
        apparaat.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="loading">Laden…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Fout bij verbinden met apparaat, we kunnen er niet mee uit te wisselen!</string>
 | 
			
		||||
    <string name="swap_not_enabled">Uitwisselen niet ingeschakeld</string>
 | 
			
		||||
 | 
			
		||||
@ -234,10 +234,6 @@
 | 
			
		||||
    <string name="swap_connecting">Łączenie</string>
 | 
			
		||||
    <string name="swap_confirm">Potwierdź wymianę</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">Kod QR który został zeskanowany nie wygląda na kod do wymiany aplikacji.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth nie jest dostępny</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Nie udało się wysłać F-Droida ponieważ Bluetooth nie jest dostępny na
 | 
			
		||||
        Twoim urządzeniu.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="loading">Ładowanie…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Wystąpił błąd podczas łączenia się z urządzeniem wymiana nie powiodła się!</string>
 | 
			
		||||
    <string name="swap_not_enabled">Wymiana nie jest włączona</string>
 | 
			
		||||
 | 
			
		||||
@ -250,10 +250,6 @@
 | 
			
		||||
    <string name="swap_connecting">Conectando</string>
 | 
			
		||||
    <string name="swap_confirm">Confirmar a permuta</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">O código QR que você escaneou não parece com um código de permuta.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth não está disponível</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Não é possível enviar F-Droid, porque o Bluetooth não está disponível
 | 
			
		||||
        neste dispositivo.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="loading">Carregando…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Ocorreu um erro ao conectar o dispositivo, não foi possível trocar com ele!</string>
 | 
			
		||||
    <string name="swap_not_enabled">Permuta não habilitada</string>
 | 
			
		||||
 | 
			
		||||
@ -301,10 +301,6 @@
 | 
			
		||||
    <string name="swap_no_peers_nearby">Não foram encontradas pessoas na sua vizinhança.</string>
 | 
			
		||||
    <string name="swap_confirm">Confirmação de troca</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">O código QR digitalizado não parece ser um código de troca.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth não disponível</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Não pode enviar o F-Droid porque o Bluetooth não está ativo no
 | 
			
		||||
        dispositivo.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Ocorreu um erro ao estabelecer a ligação e não será possível a troca!</string>
 | 
			
		||||
    <string name="swap_not_enabled">Troca não ativa</string>
 | 
			
		||||
    <string name="swap_not_enabled_description">Antes de trocar, o seu dispositivo tem que estar disponível.</string>
 | 
			
		||||
 | 
			
		||||
@ -259,7 +259,6 @@
 | 
			
		||||
    <string name="useTor">Folosește Tor</string>
 | 
			
		||||
    <string name="repo_details">Depozit</string>
 | 
			
		||||
    <string name="swap_stopping_wifi">Oprire Wi-Fi…</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth indisponibil</string>
 | 
			
		||||
    <string name="repo_provider">Depozit: %s</string>
 | 
			
		||||
 | 
			
		||||
    <string name="update_auto_download">Descarcă automat actualizările</string>
 | 
			
		||||
@ -451,7 +450,6 @@
 | 
			
		||||
    <string name="swap_view_available_networks">Atinge pentru a vedea rețelele deschise disponibile</string>
 | 
			
		||||
    <string name="swap_scan_or_type_url">O persoană trebuie să scaneze codul sau să introducă URL-ul celuilalt într-un browser.</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">Codul QR scanat nu arată ca un cod de schimb.</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Nu s-a putut trimite F-Droid, deoarece conexiunea Bluetooth nu este valabilă pe acest dispozitiv.</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Eroare produsă în timpul conectării la dispozitiv, nu se poate face schimbul!</string>
 | 
			
		||||
    <string name="swap_not_enabled_description">Înaintea schimbului, dispozitivul tău trebuie făcut vizibil.</string>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -308,8 +308,6 @@
 | 
			
		||||
    <string name="swap_no_peers_nearby">Не удается найти людей рядом, чтобы обменяться с ними.</string>
 | 
			
		||||
    <string name="swap_confirm">Подтвердить обмен</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">QR-код, который вы отсканировали, не похож на код обмена.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth не доступен</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Не удается отправить F-Droid, так как Bluetooth не доступен на данном устройстве.</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">При подключении к устройству произошла ошибка, невозможно провести с ним обмен!</string>
 | 
			
		||||
    <string name="swap_not_enabled">Обмен не включен</string>
 | 
			
		||||
    <string name="swap_not_enabled_description">Для выполнения обмена следует сделать ваше устройство видимым.</string>
 | 
			
		||||
 | 
			
		||||
@ -252,10 +252,6 @@
 | 
			
		||||
    <string name="swap_connecting">Connetende</string>
 | 
			
		||||
    <string name="swap_confirm">Cunfirma cumpartzidura</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">Su còdighe QR iscansidu non paret unu còdighe de cumpartzidura.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth non disponìbile</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Impossìbile imbiare F-Droid, ca su Bluetooth no est disponìbile in custu
 | 
			
		||||
        dispositivu.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="loading">Carrighende…</string>
 | 
			
		||||
    <string name="swap_not_enabled">Cumpartzidura non abilitada</string>
 | 
			
		||||
    <string name="swap_not_enabled_description">In antis de cumpartzire, su dispositivu depet èssere postu visìbile.</string>
 | 
			
		||||
 | 
			
		||||
@ -232,7 +232,6 @@
 | 
			
		||||
    <string name="swap_cant_find_peers">Nemôžete nájsť koho hľadáte?</string>
 | 
			
		||||
    <string name="swap_send_fdroid">Poslať F-Droid</string>
 | 
			
		||||
    <string name="swap_connecting">Pripájam</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth nedostupný</string>
 | 
			
		||||
    <string name="loading">Načítavam…</string>
 | 
			
		||||
    <string name="install_confirm">vyžaduje prístup k</string>
 | 
			
		||||
    <string name="allPerms">Všetky</string>
 | 
			
		||||
@ -257,9 +256,6 @@
 | 
			
		||||
    <string name="category_Theming">Témy</string>
 | 
			
		||||
    <string name="swap_not_visible_bluetooth">Nie je viditeľný cez Bluetooth</string>
 | 
			
		||||
    <string name="swap_not_visible_wifi">Nie je viditeľný cez Wi-Fi</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Nemožno poslať F-Droid, pretože Bluetooh nie je dostupný na tomto
 | 
			
		||||
        zariadení.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="perm_costs_money">Môže stáť peniaze</string>
 | 
			
		||||
    <string name="menu_flattr">Flattr</string>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -344,10 +344,6 @@
 | 
			
		||||
    <string name="swap_connecting">Kuhakira</string>
 | 
			
		||||
    <string name="swap_confirm">Tsinhira kutsinhana</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">Murau weQR wawanzvera hausi kuita kunge murau wekutsinhana.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth haipo</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Hatisi kukwanisa kutumira F-Droid, nekuti Bluetooth haipo pamuchina
 | 
			
		||||
        uyu.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="loading">Kuzadza…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Pane kanganiso yaitika pakuhakira kumuchina, hatisi kukwanisa kutsinhana
 | 
			
		||||
        nayo.
 | 
			
		||||
 | 
			
		||||
@ -206,7 +206,6 @@
 | 
			
		||||
    <string name="swap_send_fdroid">Пошаљи Ф-дроид</string>
 | 
			
		||||
    <string name="swap_connecting">Повезујем се</string>
 | 
			
		||||
    <string name="swap_confirm">Потврди размену</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Блутут није доступан</string>
 | 
			
		||||
    <string name="loading">Учитавам…</string>
 | 
			
		||||
    <string name="swap_not_enabled">Размена није укључена</string>
 | 
			
		||||
    <string name="newPerms">Ново</string>
 | 
			
		||||
@ -238,8 +237,6 @@
 | 
			
		||||
 | 
			
		||||
    <string name="swap_not_enabled_description">Пре размене, ваш уређај мора бити видљив.</string>
 | 
			
		||||
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Не могу да пошаљем Ф-дроида, блутут није доступан на овом уређају.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="swap_visible_bluetooth">Видљив преко блутута</string>
 | 
			
		||||
    <string name="swap_not_visible_bluetooth">Нисам видљив преко блутута</string>
 | 
			
		||||
    <string name="swap_visible_wifi">Видљив преко бежичног</string>
 | 
			
		||||
 | 
			
		||||
@ -266,8 +266,6 @@
 | 
			
		||||
    <string name="swap_connecting">Ansluter</string>
 | 
			
		||||
    <string name="swap_confirm">Bekräfta utbyte</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">QR-koden du läst av ser inte ut som en utbytarkod.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth inte tillgänglig</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Det går inte att skicka F-Droid eftersom Bluetooth är otillgänglig på den här enheten.</string>
 | 
			
		||||
    <string name="loading">Öppnar…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Fel uppstod under enhetsanslutning, går inte att utföra utbyte med den!</string>
 | 
			
		||||
    <string name="swap_not_enabled">Utbyta inte aktiverat</string>
 | 
			
		||||
 | 
			
		||||
@ -176,8 +176,6 @@
 | 
			
		||||
    <string name="swap_send_fdroid">ส่งต่อโปรแกรม F-Droid</string>
 | 
			
		||||
    <string name="swap_connecting">กำลังเชื่อมต่อ</string>
 | 
			
		||||
    <string name="swap_confirm">ยืนยันการส่งต่อ</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">ไม่สามารถใช้บลูทูธได้</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">ไม่สามารถส่งต่อโปรแกรม F-Droid ได้ เนื่องจากไม่สามารถใช้บลูทูธ</string>
 | 
			
		||||
    <string name="loading">กำลังโหลด…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">การเชื่อมต่อกับอีกเครื่องล้มเหลว, ไม่สามารถส่งต่อโปรแกรมได้</string>
 | 
			
		||||
    <string name="swap_not_enabled">ไม่เปิดใช้การส่งต่อโปรแกรม</string>
 | 
			
		||||
 | 
			
		||||
@ -261,8 +261,6 @@
 | 
			
		||||
    <string name="swap_connecting">Bağlanıyor</string>
 | 
			
		||||
    <string name="swap_confirm">Takası onaylayın</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">Taradığınız QR kodu bir takas kodu gibi görünmüyor.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth kullanılabilir değil</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">F-Droid gönderilemiyor, çünkü bu aygıtta Bluetooth kullanılabilir değil.</string>
 | 
			
		||||
    <string name="loading">Yükleniyor…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Aygıta bağlanılırken hata oluştu, onunla takas yapılamaz!</string>
 | 
			
		||||
    <string name="swap_not_enabled">Takas etkinleştirilmemiş</string>
 | 
			
		||||
 | 
			
		||||
@ -161,10 +161,6 @@
 | 
			
		||||
    <string name="swap_wifi_device_name">Назва пристрою</string>
 | 
			
		||||
    <string name="swap_send_fdroid">Відправити F-Droid</string>
 | 
			
		||||
    <string name="swap_connecting">Підключення</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth не доступний</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Неможливо надіслати F-Droid, тому що Bluetooth не доступний на цьому
 | 
			
		||||
        пристрої.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="loading">Завантаження…</string>
 | 
			
		||||
    <string name="newPerms">Нові</string>
 | 
			
		||||
    <string name="allPerms">Всі</string>
 | 
			
		||||
 | 
			
		||||
@ -242,8 +242,6 @@
 | 
			
		||||
    <string name="swap_connecting">Đang kết nối</string>
 | 
			
		||||
    <string name="swap_confirm">Xác nhận trao đổi</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">Mã QR đã quét không phải là mã trao đổi.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Không có Bluetooth</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Không thể gửi F-Droid vì thiết bị này không có Bluetooth.</string>
 | 
			
		||||
    <string name="loading">Đang tải…</string>
 | 
			
		||||
    <string name="swap_not_enabled">Trao đổi đang tắt</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Đã xảy ra lỗi khi kết nối, không thể trao đổi ứng dụng!</string>
 | 
			
		||||
 | 
			
		||||
@ -246,8 +246,6 @@
 | 
			
		||||
    <string name="swap_connecting">连接中</string>
 | 
			
		||||
    <string name="swap_confirm">确认交换</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">你扫描的二维码似乎不是一个交换代码。</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">蓝牙不可用</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">不能发送 F-Droid,因为该设备蓝牙不可用。</string>
 | 
			
		||||
    <string name="loading">加载中…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">连接设备时出错,无法交换应用!</string>
 | 
			
		||||
    <string name="swap_not_enabled">未启用交换功能</string>
 | 
			
		||||
 | 
			
		||||
@ -209,8 +209,6 @@
 | 
			
		||||
    <string name="swap_not_enabled">沒有啟用交換功能</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">與裝置連接時發生了問題,未能進行交換!</string>
 | 
			
		||||
    <string name="loading">正在載入…</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">因為此裝置沒有藍牙功能,未能傳送 F-Droid。</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">藍牙不可用</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">您所掃描的 QR 碼不是一個交換碼。</string>
 | 
			
		||||
    <string name="swap_confirm">確定交換</string>
 | 
			
		||||
    <string name="swap_no_peers_nearby">找不到附近的人進行交換。</string>
 | 
			
		||||
 | 
			
		||||
@ -220,8 +220,6 @@
 | 
			
		||||
    <string name="swap_send_fdroid">傳送 F-Droid</string>
 | 
			
		||||
    <string name="swap_no_peers_nearby">找不到附近的人來進行交換。</string>
 | 
			
		||||
    <string name="swap_confirm">確認交換</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">藍牙不可用</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">無法傳送 F-Droid,因為此裝置沒有藍牙功能。</string>
 | 
			
		||||
    <string name="swap_not_enabled">沒有啟用交換功能</string>
 | 
			
		||||
    <string name="uninstall_confirm">您想要解除安裝此應用程式嗎?</string>
 | 
			
		||||
    <string name="download_error">下載失敗!</string>
 | 
			
		||||
 | 
			
		||||
@ -453,13 +453,14 @@ This often occurs with apps installed via Google Play or other sources, if they
 | 
			
		||||
    <string name="swap_connecting">Connecting</string>
 | 
			
		||||
    <string name="swap_confirm">Confirm swap</string>
 | 
			
		||||
    <string name="swap_qr_isnt_for_swap">The QR code you scanned doesn\'t look like a swap code.</string>
 | 
			
		||||
    <string name="bluetooth_unavailable">Bluetooth unavailable</string>
 | 
			
		||||
    <string name="swap_cant_send_no_bluetooth">Cannot send F-Droid, because Bluetooth is unavailable on this device.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="use_bluetooth">Use Bluetooth</string>
 | 
			
		||||
    <string name="loading">Loading…</string>
 | 
			
		||||
    <string name="swap_connection_misc_error">Error occurred while connecting to device, can\'t swap with it!</string>
 | 
			
		||||
    <string name="swap_not_enabled">Swapping not enabled</string>
 | 
			
		||||
    <string name="swap_not_enabled_description">Before swapping, your device must be made visible.</string>
 | 
			
		||||
    <string name="swap_toast_invalid_url">Invalid URL for swapping: %1$s</string>
 | 
			
		||||
    <string name="swap_toast_hotspot_enabled">Wi-Fi Hotspot enabled</string>
 | 
			
		||||
    <string name="swap_toast_could_not_enable_hotspot">Could not enable Wi-Fi Hotspot!</string>
 | 
			
		||||
 | 
			
		||||
    <string name="install_confirm">needs access to</string>
 | 
			
		||||
    <string name="install_confirm_update">Do you want to install an update
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,36 @@
 | 
			
		||||
package org.fdroid.fdroid.net;
 | 
			
		||||
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import org.apache.commons.net.util.SubnetUtils;
 | 
			
		||||
import org.fdroid.fdroid.BuildConfig;
 | 
			
		||||
import org.fdroid.fdroid.FDroidApp;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
import org.junit.runner.RunWith;
 | 
			
		||||
import org.robolectric.RobolectricTestRunner;
 | 
			
		||||
import org.robolectric.annotation.Config;
 | 
			
		||||
 | 
			
		||||
import java.net.MalformedURLException;
 | 
			
		||||
import java.net.URL;
 | 
			
		||||
 | 
			
		||||
import static org.junit.Assert.assertTrue;
 | 
			
		||||
import static org.junit.Assert.assertFalse;
 | 
			
		||||
 | 
			
		||||
@Config(constants = BuildConfig.class, sdk = 24)
 | 
			
		||||
@RunWith(RobolectricTestRunner.class)
 | 
			
		||||
@SuppressWarnings("LineLength")
 | 
			
		||||
public class HttpDownloaderTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testIsSwapUri() throws MalformedURLException {
 | 
			
		||||
        FDroidApp.subnetInfo = new SubnetUtils("192.168.0.112/24").getInfo();
 | 
			
		||||
        String urlString = "http://192.168.0.112:8888/fdroid/repo?fingerprint=113F56CBFA967BA825DD13685A06E35730E0061C6BB046DF88A";
 | 
			
		||||
        assertTrue(HttpDownloader.isSwapUrl("192.168.0.112", 8888)); // NOPMD
 | 
			
		||||
        assertTrue(HttpDownloader.isSwapUrl(Uri.parse(urlString)));
 | 
			
		||||
        assertTrue(HttpDownloader.isSwapUrl(new URL(urlString)));
 | 
			
		||||
 | 
			
		||||
        assertFalse(HttpDownloader.isSwapUrl("192.168.1.112", 8888)); // NOPMD
 | 
			
		||||
        assertFalse(HttpDownloader.isSwapUrl("192.168.0.112", 80)); // NOPMD
 | 
			
		||||
        assertFalse(HttpDownloader.isSwapUrl(Uri.parse("https://malware.com:8888")));
 | 
			
		||||
        assertFalse(HttpDownloader.isSwapUrl(new URL("https://www.google.com")));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user