aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/app/src/main/java/com/wireguard/android/activity/BaseActivity.java
blob: 7b6923f913c64bede01b323a104c4126e2e825f5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*
 * Copyright © 2018 Samuel Holland <samuel@sholland.org>
 * Copyright © 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

package com.wireguard.android.activity;

import android.content.Intent;
import android.databinding.CallbackRegistry;
import android.databinding.CallbackRegistry.NotifierCallback;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import com.wireguard.android.Application;
import com.wireguard.android.backend.GoBackend;
import com.wireguard.android.model.Tunnel;
import com.wireguard.android.model.TunnelManager;
import com.wireguard.android.util.Topic;

import java.lang.reflect.Field;
import java.util.Objects;

/**
 * Base class for activities that need to remember the currently-selected tunnel.
 */

public abstract class BaseActivity extends AppCompatActivity implements Topic.Subscriber {
    private static final String TAG = "WireGuard/" + BaseActivity.class.getSimpleName();

    private static final String KEY_SELECTED_TUNNEL = "selected_tunnel";

    private final SelectionChangeRegistry selectionChangeRegistry = new SelectionChangeRegistry();
    private Tunnel selectedTunnel;

    public void addOnSelectedTunnelChangedListener(
            final OnSelectedTunnelChangedListener listener) {
        selectionChangeRegistry.add(listener);
    }

    public Tunnel getSelectedTunnel() {
        return selectedTunnel;
    }

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        subscribeTopics();

        // Restore the saved tunnel if there is one; otherwise grab it from the arguments.
        String savedTunnelName = null;
        if (savedInstanceState != null)
            savedTunnelName = savedInstanceState.getString(KEY_SELECTED_TUNNEL);
        else if (getIntent() != null)
            savedTunnelName = getIntent().getStringExtra(KEY_SELECTED_TUNNEL);
        if (savedTunnelName != null) {
            final TunnelManager tunnelManager = Application.getComponent().getTunnelManager();
            selectedTunnel = tunnelManager.getTunnels().get(savedTunnelName);
        }

        // The selected tunnel must be set before the superclass method recreates fragments.
        super.onCreate(savedInstanceState);

        if (Application.getComponent().getBackendType() == GoBackend.class) {
            final Intent intent = GoBackend.VpnService.prepare(this);
            if (intent != null) {
                startActivityForResult(intent, 0);
            }
        }
    }

    @Override
    protected void onDestroy() {
        unsubscribeTopics();
        super.onDestroy();
    }

    @Override
    protected void onSaveInstanceState(final Bundle outState) {
        if (selectedTunnel != null)
            outState.putString(KEY_SELECTED_TUNNEL, selectedTunnel.getName());
        super.onSaveInstanceState(outState);
    }

    protected abstract void onSelectedTunnelChanged(Tunnel oldTunnel, Tunnel newTunnel);

    public void removeOnSelectedTunnelChangedListener(
            final OnSelectedTunnelChangedListener listener) {
        selectionChangeRegistry.remove(listener);
    }

    public void setSelectedTunnel(final Tunnel tunnel) {
        final Tunnel oldTunnel = selectedTunnel;
        if (Objects.equals(oldTunnel, tunnel))
            return;
        selectedTunnel = tunnel;
        onSelectedTunnelChanged(oldTunnel, tunnel);
        selectionChangeRegistry.notifyCallbacks(oldTunnel, 0, tunnel);
    }

    public interface OnSelectedTunnelChangedListener {
        void onSelectedTunnelChanged(Tunnel oldTunnel, Tunnel newTunnel);
    }

    private static final class SelectionChangeNotifier
            extends NotifierCallback<OnSelectedTunnelChangedListener, Tunnel, Tunnel> {
        @Override
        public void onNotifyCallback(final OnSelectedTunnelChangedListener listener,
                                     final Tunnel oldTunnel, final int ignored,
                                     final Tunnel newTunnel) {
            listener.onSelectedTunnelChanged(oldTunnel, newTunnel);
        }
    }

    private static final class SelectionChangeRegistry
            extends CallbackRegistry<OnSelectedTunnelChangedListener, Tunnel, Tunnel> {
        private SelectionChangeRegistry() {
            super(new SelectionChangeNotifier());
        }
    }

    @Override
    public void onTopicPublished(Topic topic) {
        try {
            Field f = getResources().getClass().getDeclaredField("mResourcesImpl");
            f.setAccessible(true);
            Object o = f.get(getResources());
            f = o.getClass().getDeclaredField("mDrawableCache");
            f.setAccessible(true);
            o = f.get(o);
            f = o.getClass().getSuperclass().getDeclaredField("mThemedEntries");
            f.setAccessible(true);
            o = f.get(o);
            o.getClass().getMethod("clear").invoke(o);
        } catch (Exception e) {
            Log.e(TAG, "Failed to flush icon cache", e);
        }
        recreate();
    }

    @Override
    public Topic[] getSubscription() {
        return new Topic[] { Application.getComponent().getThemeChangeTopic() };
    }
}