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

Java tutorial

Introduction

Here is the source code for syncthing.android.ui.sessionsettings.SettingsPresenter.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.content.Context;
import android.databinding.Bindable;
import android.os.Bundle;
import android.support.design.widget.CoordinatorLayout;
import android.support.v7.app.AlertDialog;
import android.view.MenuItem;
import android.view.View;
import android.widget.PopupMenu;

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

import javax.inject.Inject;

import rx.functions.Action1;
import syncthing.android.R;
import syncthing.android.service.SyncthingUtils;
import syncthing.android.settings.AppSettings;
import syncthing.api.Credentials;
import syncthing.api.SessionManager;
import syncthing.api.model.Config;
import syncthing.api.model.DeviceConfig;
import syncthing.api.model.GUIConfig;
import syncthing.api.model.OptionsConfig;
import timber.log.Timber;

/**
 * Created by drew on 3/17/15.
 */
@ScreenScope
public class SettingsPresenter extends EditPresenter<CoordinatorLayout> {

    final Context appContext;
    final AppSettings appSettings;

    DeviceConfig thisDevice;
    OptionsConfig options;
    GUIConfig guiConfig;

    String hiddenPass;

    String errorListenAddress;
    String errorGlobalDiscoverServers;
    String errorGuiListenAddress;

    @Inject
    public SettingsPresenter(SessionManager manager, DialogPresenter dialogPresenter,
            ActivityResultsController activityResultContoller, ToolbarOwner toolbarOwner,
            EditPresenterConfig config, @ForApplication Context appContext, AppSettings appSettings) {
        super(manager, dialogPresenter, activityResultContoller, toolbarOwner, config);
        this.appContext = appContext;
        this.appSettings = appSettings;
    }

    @Override
    protected void onLoad(Bundle savedInstanceState) {
        super.onLoad(savedInstanceState);
        if (!wasPreviouslyLoaded && savedInstanceState != null) {
            thisDevice = (DeviceConfig) savedInstanceState.getSerializable("device");
            options = (OptionsConfig) savedInstanceState.getSerializable("options");
            guiConfig = (GUIConfig) savedInstanceState.getSerializable("guiconfig");
        } else if (!wasPreviouslyLoaded) {
            DeviceConfig d = controller.getThisDevice();
            if (d != null) {
                thisDevice = d.clone();
            }
            Config config = controller.getConfig();
            if (config != null) {
                OptionsConfig o = config.options;
                if (o != null) {
                    options = o.clone();
                }
                GUIConfig g = config.gui;
                if (g != null) {
                    guiConfig = g.clone();
                }
            }
        }
        wasPreviouslyLoaded = true;
        if (thisDevice == null || options == null || guiConfig == null) {
            Timber.e("Incomplete data! Cannot continue");
            dismissDialog();
        }
        hiddenPass = getView().getContext().getString(R.string.ten_stars);
    }

    @Override
    protected void onSave(Bundle outState) {
        super.onSave(outState);
        //TODO parcelable
        outState.putSerializable("device", thisDevice);
        outState.putSerializable("options", options);
        outState.putSerializable("guiconfig", guiConfig);
    }

    @Bindable
    public String getDeviceName() {
        return SyncthingUtils.getDisplayName(thisDevice);
    }

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

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

    @Bindable
    public String getListenAddressText() {
        return SyncthingUtils.unrollArray(options.listenAddress);
    }

    public void setListenAddress(CharSequence text) {
        if (validateListenAddresses(text)) {
            options.listenAddress = SyncthingUtils.rollArray(text.toString());
        }
    }

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

    @Bindable
    public String getListenAddressError() {
        return errorListenAddress;
    }

    public void setListenAddressError(String error) {
        if (!StringUtils.equals(errorListenAddress, error)) {
            errorListenAddress = error;
            notifyChange(syncthing.android.BR.listenAddressError);
        }
    }

    boolean validateListenAddresses(CharSequence text) {
        int err = 0;
        if (StringUtils.isEmpty(text)) {
            err = R.string.input_error;
        } else {
            String string = StringUtils.trim(text.toString());
            if (!StringUtils.startsWith(string, "tcp://")) {
                err = R.string.input_error;
            } //todo ip w/ port or ?
        }
        if (hasView()) {
            String msg = err != 0 ? getView().getContext().getString(err) : null;
            setGuiListenAddressError(msg);
        }
        return err == 0;
    }

    @Bindable
    public String getMaxRecvKbps() {
        return String.valueOf(options.maxRecvKbps);
    }

    public void setMaxRecvKpbs(CharSequence text) {
        //input disallows non numeric text
        options.maxRecvKbps = StringUtils.isEmpty(text) ? 0 : Integer.valueOf(text.toString());
    }

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

    @Bindable
    public String getMaxSendKbps() {
        return String.valueOf(options.maxSendKbps);
    }

    public void setMaxSendKbps(CharSequence text) {
        //input disallows non numeric text
        options.maxSendKbps = StringUtils.isEmpty(text) ? 0 : Integer.valueOf(text.toString());
    }

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

    @Bindable
    public boolean isNatEnabled() {
        return options.natEnabled;
    }

    public void setNatEnabled(boolean enabled) {
        options.natEnabled = enabled;
    }

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

    @Bindable
    public boolean isLocalAnnounceEnabled() {
        return options.localAnnounceEnabled;
    }

    public void setLocalAnnounceEnabled(boolean enabled) {
        options.localAnnounceEnabled = enabled;
    }

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

    @Bindable
    public boolean isGlobalAnnounceEnabled() {
        return options.globalAnnounceEnabled;
    }

    public void setGlobalAnnounceEnabled(boolean enabled) {
        options.globalAnnounceEnabled = enabled;
        notifyChange(syncthing.android.BR.globalAnnounceEnabled);
    }

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

    @Bindable
    public String getGlobalAnnounceServersText() {
        return SyncthingUtils.unrollArray(options.globalAnnounceServers);
    }

    public void setGlobalAnnounceServers(CharSequence text) {
        if (validateGlobalDiscoveryServers(text)) {
            options.globalAnnounceServers = SyncthingUtils.rollArray(text.toString());
        }
    }

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

    @Bindable
    public String getGlobalAnnounceServersError() {
        return errorGlobalDiscoverServers;
    }

    public void setGlobalAnnounceServersError(String text) {
        if (!StringUtils.equals(errorGlobalDiscoverServers, text)) {
            errorGlobalDiscoverServers = text;
            notifyChange(syncthing.android.BR.globalAnnounceServersError);
        }
    }

    boolean validateGlobalDiscoveryServers(CharSequence text) {
        int err = 0;
        if (StringUtils.isEmpty(text)) {
            err = R.string.input_error;
        } else {
            //TODO dynamic dynamic-v4 dynamic-v6 or hosts or ips
            // (too many combinations so just use user discretion)
        }
        if (hasView()) {
            String msg = err != 0 ? getView().getContext().getString(err) : null;
            setGuiListenAddressError(msg);
        }
        return err == 0;
    }

    @Bindable
    public String getGuiListenAddress() {
        return guiConfig.address;
    }

    public void setGuiListenAddress(CharSequence text) {
        if (validateGuiListenAddress(text)) {
            guiConfig.address = text.toString();
        }
    }

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

    @Bindable
    public String getGuiListenAddressError() {
        return errorGuiListenAddress;
    }

    public void setGuiListenAddressError(String text) {
        if (!StringUtils.equals(errorGuiListenAddress, text)) {
            errorGuiListenAddress = text;
            notifyChange(syncthing.android.BR.guiListenAddressError);
        }
    }

    boolean validateGuiListenAddress(CharSequence text) {
        int err = 0;
        if (StringUtils.isEmpty(text)) {
            err = R.string.input_error;
        } else {
            String string = StringUtils.trim(text.toString());
            if (!SyncthingUtils.isIpAddressWithPort(string)) {
                err = R.string.input_error;
            }
        }
        if (hasView()) {
            String msg = err != 0 ? getView().getContext().getString(err) : null;
            setGuiListenAddressError(msg);
        }
        return err == 0;
    }

    @Bindable
    public String getGuiUser() {
        return guiConfig.user;
    }

    public void setGuiUser(CharSequence text) {
        guiConfig.user = StringUtils.isEmpty(text) ? "" : text.toString();
    }

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

    public void setGuiPassword(CharSequence text) {
        if (StringUtils.isEmpty(text)) {
            guiConfig.password = "";
        } else if (!StringUtils.equals(hiddenPass, text.toString())) {
            guiConfig.password = text.toString();
        }
    }

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

    @Bindable
    public boolean isUseTLS() {
        return guiConfig.useTLS;
    }

    public void setUseTLS(boolean enable) {
        guiConfig.useTLS = enable;
    }

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

    @Bindable
    public boolean isStartBrowser() {
        return options.startBrowser;
    }

    public void setStartBrowser(boolean enable) {
        options.startBrowser = enable;
    }

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

    @Bindable
    public boolean isURAccepted() {
        return options.urAccepted > 0;
    }

    public void setURAccepted(boolean enable) {
        options.urAccepted = enable ? 1 : -1;
    }

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

    @Bindable
    public String getApiKey() {
        return guiConfig.apiKey;
    }

    public void setApiKey(String text) {
        guiConfig.apiKey = text;
    }

    @Bindable
    public boolean isHasClipboard() {
        return !SyncthingUtils.isClipBoardSupported(appContext);
    }

    public void showApiKeyOverflow(final View btn) {
        PopupMenu m = new PopupMenu(btn.getContext(), btn);
        m.inflate(R.menu.apikey_overflow);
        m.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                switch (item.getItemId()) {
                case R.id.copy:
                    copyApiKey(btn);
                    return true;
                case R.id.generate:
                    regenApiKey(btn);
                    return true;
                default:
                    return false;
                }
            }
        });
        m.show();
    }

    public void copyApiKey(View btn) {
        SyncthingUtils.copyToClipboard(btn.getContext(), btn.getContext().getString(R.string.api_key), getApiKey());
    }

    public void regenApiKey(View btn) {
        String key = SyncthingUtils.randomString(32);
        setApiKey(key);
        notifyChange(syncthing.android.BR.apiKey);
    }

    public void saveConfig(View btn) {
        boolean invalid = false;
        invalid |= errorListenAddress != null;
        invalid |= errorGlobalDiscoverServers != null;
        invalid |= errorGuiListenAddress != 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) -> {
                        saveConfig();
                    }).create());
        } else {
            saveConfig();
        }
    }

    private void saveConfig() {
        unsubscribe(saveSubscription);
        onSaveStart();
        final String deviceName = thisDevice.name;
        saveSubscription = controller.editSettings(thisDevice, options, guiConfig, this::onSavefailed, () -> {
            maybeUpdateAlias(deviceName);
            onSaveSuccessfull();
        });
    }

    void maybeUpdateAlias(String deviceName) {
        if (!StringUtils.isEmpty(deviceName) && !StringUtils.equalsIgnoreCase(credentials.alias, deviceName)) {
            appSettings.saveCredentials(new Credentials(deviceName, credentials.id, credentials.url,
                    credentials.apiKey, credentials.caCert));
        }
    }
}