syncthing.android.ui.sessionsettings.EditDevicePresenter.java Source code

Java tutorial

Introduction

Here is the source code for syncthing.android.ui.sessionsettings.EditDevicePresenter.java

Source

/*
 * Copyright (c) 2015 OpenSilk Productions LLC
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package syncthing.android.ui.sessionsettings;

import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.databinding.Bindable;
import android.databinding.BindingAdapter;
import android.os.Bundle;
import android.support.design.widget.CoordinatorLayout;
import android.support.v7.app.AlertDialog;
import android.view.View;
import android.widget.CheckBox;
import android.widget.LinearLayout;

import com.jakewharton.rxbinding.widget.RxCompoundButton;

import org.apache.commons.lang3.StringUtils;
import org.opensilk.common.core.dagger2.ScreenScope;
import org.opensilk.common.ui.mortar.ActivityResultsController;
import org.opensilk.common.ui.mortar.ActivityResultsListener;
import org.opensilk.common.ui.mortar.DialogPresenter;
import org.opensilk.common.ui.mortar.ToolbarOwner;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.inject.Inject;

import mortar.MortarScope;
import rx.Subscription;
import rx.functions.Action1;
import syncthing.android.R;
import syncthing.android.service.SyncthingUtils;
import syncthing.android.ui.common.ActivityRequestCodes;
import syncthing.api.SessionManager;
import syncthing.api.model.Compression;
import syncthing.api.model.DeviceConfig;
import syncthing.api.model.FolderConfig;
import syncthing.api.model.FolderDeviceConfig;
import timber.log.Timber;

/**
 * Created by drew on 3/16/15.
 */
@ScreenScope
public class EditDevicePresenter extends EditPresenter<CoordinatorLayout> implements ActivityResultsListener {

    DeviceConfig originalDevice;
    TreeMap<String, Boolean> sharedFolders;

    Subscription deleteSubscription;

    String errorDeviceId;
    String errorAddresses;

    @Inject
    public EditDevicePresenter(SessionManager manager, DialogPresenter dialogPresenter,
            ActivityResultsController activityResultContoller, ToolbarOwner toolbarOwner,
            EditPresenterConfig config) {
        super(manager, dialogPresenter, activityResultContoller, toolbarOwner, config);
    }

    @Override
    protected void onEnterScope(MortarScope scope) {
        super.onEnterScope(scope);
        activityResultsController.register(scope, this);
    }

    @Override
    protected void onExitScope() {
        super.onExitScope();
        unsubscribe(deleteSubscription);
    }

    @Override
    @SuppressWarnings("unchecked")
    protected void onLoad(Bundle savedInstanceState) {
        super.onLoad(savedInstanceState);
        if (!wasPreviouslyLoaded && savedInstanceState != null) {
            originalDevice = (DeviceConfig) savedInstanceState.getSerializable("device");
            sharedFolders = (TreeMap<String, Boolean>) savedInstanceState.getSerializable("folders");
        } else if (!wasPreviouslyLoaded) {
            if (isAdd) {
                originalDevice = DeviceConfig.withDefaults();
            } else {
                DeviceConfig d = controller.getDevice(deviceId);
                if (d != null) {
                    originalDevice = d.clone();
                }
            }
            sharedFolders = new TreeMap<>();
            List<FolderConfig> folders = controller.getFolders();
            for (FolderConfig f : folders) {
                sharedFolders.put(f.id, false);
                if (!StringUtils.isEmpty(getDeviceID())) {
                    for (FolderDeviceConfig d : f.devices) {
                        if (StringUtils.equals(d.deviceID, getDeviceID())) {
                            sharedFolders.put(f.id, true);
                            break;
                        }
                    }
                }
            }
        }
        wasPreviouslyLoaded = true;
        if (originalDevice == null) {
            Timber.e("Null device! Cannot continue");
            dismissDialog();
        }
    }

    @Override
    protected void onSave(Bundle outState) {
        super.onSave(outState);
        outState.putSerializable("device", originalDevice);
        outState.putSerializable("folders", sharedFolders);
    }

    @Bindable
    public boolean isAdd() {
        return isAdd;
    }

    @Bindable
    public String getDeviceID() {
        return originalDevice.deviceID;
    }

    public void setDeviceID(CharSequence deviceID) {
        if (validateDeviceId(deviceID, true)) {
            originalDevice.deviceID = deviceID.toString();
        }
    }

    public final Action1<CharSequence> actionSetDeviceID = new Action1<CharSequence>() {
        @Override
        public void call(CharSequence charSequence) {
            setDeviceID(charSequence);
        }
    };

    @Bindable
    public String getDeviceIDError() {
        return errorDeviceId;
    }

    public void setDeviceIDError(String error) {
        if (!StringUtils.equals(errorDeviceId, error)) {
            errorDeviceId = error;
            notifyChange(syncthing.android.BR.deviceIDError);
        }
    }

    boolean validateDeviceId(CharSequence text, boolean strict) {
        int e = 0;
        if (StringUtils.isEmpty(text)) {
            e = R.string.the_device_id_cannot_be_blank;
        } else if (strict && !StringUtils.remove(text.toString(), ' ').matches(("^[- \\w\\s]{50,64}$"))) {
            e = R.string.the_entered_device_id_does_not_look_valid_it_should_be_a_52_or_56_character_string_consisting_of_letters_and_numbers_with_spaces_and_dashes_being_optional;
        }
        if (hasView()) {
            setDeviceIDError(e != 0 ? getView().getContext().getString(e) : null);
        }
        return e == 0;
    }

    @Bindable
    public String getDeviceName() {
        return originalDevice.name;
    }

    public void setDeviceName(CharSequence name) {
        originalDevice.name = StringUtils.isEmpty(name) ? "" : name.toString();
    }

    public final Action1<CharSequence> actionSetDeviceName = new Action1<CharSequence>() {
        @Override
        public void call(CharSequence charSequence) {
            setDeviceName(charSequence);
        }
    };

    @Bindable
    public String getAddressesText() {
        return SyncthingUtils.unrollArray(originalDevice.addresses);
    }

    public void setAddresses(CharSequence addressesText) {
        if (validateAddresses(addressesText)) {
            originalDevice.addresses = SyncthingUtils.rollArray(addressesText.toString());
        }
    }

    public final Action1<CharSequence> actionSetAddresses = new Action1<CharSequence>() {
        @Override
        public void call(CharSequence charSequence) {
            setAddresses(charSequence);
        }
    };

    @Bindable
    public String getAddressesError() {
        return errorAddresses;
    }

    public void setAddressesError(String text) {
        if (!StringUtils.equals(errorAddresses, text)) {
            errorAddresses = text;
            notifyChange(syncthing.android.BR.addressesError);
        }
    }

    boolean validateAddresses(CharSequence text) {
        boolean invalid = false;
        if (StringUtils.isEmpty(text.toString())) {
            invalid = true;
        } else {
            String s = text.toString().trim();
            if (!StringUtils.contains(s, ',')) {
                if (!StringUtils.equals(s, "dynamic") && !validateAddress(s)) {
                    invalid = true;
                }
            } else {
                String[] addrs = SyncthingUtils.rollArray(s);
                for (String addr : addrs) {
                    if (!validateAddress(addr.trim())) {
                        invalid = true;
                        break;
                    }
                }
            }
        }
        if (hasView()) {
            setAddressesError(invalid ? getView().getContext().getString(R.string.input_error) : null);
        }
        return !invalid;
    }

    boolean validateAddress(String addr) {
        return StringUtils.startsWith(addr.trim(), "tcp://")
                && (SyncthingUtils.isIpAddressWithPort(StringUtils.removeStart(addr.trim(), "tcp://"))
                        || SyncthingUtils.isDomainNameWithPort(StringUtils.removeStart(addr.trim(), "tcp://")));
    }

    @Bindable
    public Compression getCompression() {
        return originalDevice.compression;
    }

    public void setCompression(Compression compression) {
        originalDevice.compression = compression;
    }

    public final Action1<Integer> actionOnCompressionChanged = new Action1<Integer>() {
        @Override
        public void call(Integer checkedId) {
            switch (checkedId) {
            case R.id.radio_all_compression:
                setCompression(Compression.ALWAYS);
                break;
            case R.id.radio_meta_compression:
                setCompression(Compression.METADATA);
                break;
            case R.id.radio_no_compression:
                setCompression(Compression.NEVER);
                break;
            }
        }
    };

    @Bindable
    public boolean isIntroducer() {
        return originalDevice.introducer;
    }

    public void setIntroducer(boolean introducer) {
        originalDevice.introducer = introducer;
    }

    public final Action1<Boolean> actionSetIntroducer = new Action1<Boolean>() {
        @Override
        public void call(Boolean aBoolean) {
            setIntroducer(aBoolean);
        }
    };

    public void setFolderShared(String folderId, boolean shared) {
        sharedFolders.put(folderId, shared);
    }

    @BindingAdapter("addShareFolders")
    public static void addSharedFolders(LinearLayout shareFoldersContainer, EditDevicePresenter presenter) {
        if (presenter == null)
            return;
        shareFoldersContainer.removeAllViews();
        for (Map.Entry<String, Boolean> e : presenter.sharedFolders.entrySet()) {
            final String id = e.getKey();
            CheckBox checkBox = new CheckBox(shareFoldersContainer.getContext());
            checkBox.setText(id);
            checkBox.setChecked(e.getValue());
            shareFoldersContainer.addView(checkBox);
            presenter.bindingSubscriptions().add(RxCompoundButton.checkedChanges(checkBox).subscribe(checked -> {
                presenter.setFolderShared(id, checked);
            }));
        }
    }

    public void saveDevice(View btn) {
        boolean invalid = false;
        invalid |= errorDeviceId != null;
        invalid |= errorAddresses != null;
        if (invalid) {
            dialogPresenter.showDialog(context -> new AlertDialog.Builder(context).setTitle(R.string.input_error)
                    .setMessage(R.string.input_error_message).setPositiveButton(android.R.string.cancel, null)
                    .setNegativeButton(R.string.save, (d, w) -> {
                        saveDevice();
                    }).create());
        } else {
            saveDevice();
        }
    }

    private void saveDevice() {
        unsubscribe(saveSubscription);
        onSaveStart();
        saveSubscription = controller.editDevice(originalDevice, sharedFolders, this::onSavefailed,
                this::onSaveSuccessfull);
    }

    public void deleteDevice(View btn) {
        unsubscribe(deleteSubscription);
        onSaveStart();
        deleteSubscription = controller.deleteDevice(originalDevice, this::onSavefailed, this::onSaveSuccessfull);
    }

    public void startQRScannerActivity(View btn) {
        Intent intentScan = new Intent("com.google.zxing.client.android.SCAN");
        intentScan.addCategory(Intent.CATEGORY_DEFAULT);
        intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
        try {
            activityResultsController.startActivityForResult(intentScan, ActivityRequestCodes.SCAN_QR, null);
        } catch (ActivityNotFoundException e) {
            if (hasView()) {
                dialogPresenter.showDialog(context -> new AlertDialog.Builder(context).setTitle(R.string.error)
                        .setMessage(R.string.no_qr_scanner_installed).setPositiveButton(android.R.string.ok, null)
                        .create());
            }
        }
    }

    @Override
    public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == ActivityRequestCodes.SCAN_QR) {
            if (resultCode == Activity.RESULT_OK && hasView()) {
                String id = data.getStringExtra("SCAN_RESULT");
                if (!StringUtils.isEmpty(id)) {
                    setDeviceID(id);
                    notifyChange(syncthing.android.BR.deviceID);
                }
            }
            return true;
        }
        return false;
    }
}