pairs = list(
38 | new BasicNameValuePair("grant_type", "password"),
39 | new BasicNameValuePair("username", username),
40 | new BasicNameValuePair("password", password));
41 | return URLEncodedUtils.format(pairs, HTTP.UTF_8);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/SparkCore/src/io/spark/core/android/cloud/login/TokenResponse.java:
--------------------------------------------------------------------------------
1 | package io.spark.core.android.cloud.login;
2 |
3 | import io.spark.core.android.util.Strings;
4 |
5 |
6 | public class TokenResponse {
7 |
8 | // only available when request is successful (HTTP 200)
9 | public final String accessToken;
10 | public final String tokenType;
11 | public final int expiresIn;
12 |
13 | // all(?) other responses
14 | public final String errorDescription;
15 |
16 | private int statusCode;
17 |
18 |
19 | public TokenResponse(String accessToken, String tokenType, int expiresIn,
20 | String errorDescription) {
21 | this.accessToken = accessToken;
22 | this.tokenType = tokenType;
23 | this.expiresIn = expiresIn;
24 | this.errorDescription = errorDescription;
25 | }
26 |
27 | public TokenResponse() {
28 | this(null, null, -1, null);
29 | }
30 |
31 | @Override
32 | public String toString() {
33 | return "LoginResponse [accessToken=" + Strings.getRedacted(accessToken) + ", tokenType="
34 | + tokenType + ", expiresIn=" + expiresIn + ", errorDescription=" + errorDescription
35 | + "]";
36 | }
37 |
38 | public int getStatusCode() {
39 | return statusCode;
40 | }
41 |
42 | public void setStatusCode(int statusCode) {
43 | this.statusCode = statusCode;
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/SparkCore/src/io/spark/core/android/cloud/login/TokenTool.java:
--------------------------------------------------------------------------------
1 | package io.spark.core.android.cloud.login;
2 |
3 | import io.spark.core.android.app.AppConfig;
4 | import io.spark.core.android.cloud.ApiUrlHelper;
5 |
6 | import java.io.BufferedInputStream;
7 | import java.io.BufferedOutputStream;
8 | import java.io.BufferedReader;
9 | import java.io.IOException;
10 | import java.io.InputStream;
11 | import java.io.InputStreamReader;
12 | import java.io.OutputStream;
13 | import java.io.UnsupportedEncodingException;
14 | import java.net.HttpURLConnection;
15 | import java.net.URL;
16 |
17 | import org.apache.http.protocol.HTTP;
18 | import org.solemnsilence.util.TLog;
19 |
20 | import android.net.Uri;
21 | import android.util.Base64;
22 |
23 | import com.google.gson.Gson;
24 | import com.squareup.okhttp.OkHttpClient;
25 |
26 |
27 | public class TokenTool {
28 |
29 | private static final TLog log = new TLog(TokenTool.class);
30 |
31 | private static final String[] PATH_SEGMENTS = new String[] { "oauth", "token" };
32 |
33 |
34 | private final Gson gson;
35 | private final OkHttpClient okHttpclient;
36 |
37 | public TokenTool(Gson gson, OkHttpClient okHttpclient) {
38 | this.gson = gson;
39 | this.okHttpclient = okHttpclient;
40 | }
41 |
42 |
43 | public TokenResponse requestToken(TokenRequest tokenRequest) {
44 | // URL url = ApiUrlHelper.buildUrlNoVersion(PATH);
45 | Uri.Builder uriBuilder = ApiUrlHelper.getBaseUriBuilder();
46 | for (String pathSegment : PATH_SEGMENTS) {
47 | uriBuilder.appendPath(pathSegment);
48 | }
49 | URL url = ApiUrlHelper.convertToURL(uriBuilder);
50 | HttpURLConnection urlConnection = null;
51 | try {
52 | urlConnection = okHttpclient.open(url);
53 | return requestTokenPrivate(urlConnection, tokenRequest);
54 |
55 | } catch (Exception e) {
56 | log.e("Error when logging in");
57 | return null;
58 |
59 | } finally {
60 | if (urlConnection != null) {
61 | urlConnection.disconnect();
62 | }
63 | }
64 | }
65 |
66 | private TokenResponse requestTokenPrivate(HttpURLConnection urlConnection,
67 | TokenRequest tokenRequest) {
68 |
69 | TokenResponse response = new TokenResponse();
70 | int responseCode = -1;
71 |
72 | urlConnection.setDoOutput(true);
73 | urlConnection.setConnectTimeout(5000);
74 | urlConnection.setReadTimeout(15000);
75 | urlConnection.setRequestProperty("Authorization", getBasicAuthString());
76 |
77 | try {
78 | OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
79 | out.write(tokenRequest.asFormEncodedData().getBytes(HTTP.UTF_8));
80 | out.close();
81 |
82 | responseCode = urlConnection.getResponseCode();
83 |
84 | InputStream in = new BufferedInputStream(urlConnection.getInputStream());
85 | String responseStr = readStream(in);
86 | in.close();
87 | if (responseStr == null) {
88 | log.e("Error logging in, response was null. HTTP response: " + responseCode);
89 | return null;
90 | } else {
91 | response = gson.fromJson(responseStr, TokenResponse.class);
92 | }
93 | } catch (IOException e) {
94 | log.e("Error requesting token");
95 | }
96 |
97 | response.setStatusCode(responseCode);
98 | return response;
99 | }
100 |
101 | private String getBasicAuthString() {
102 | try {
103 | byte[] asBytes = AppConfig.getSparkTokenCreationCredentials().getBytes(HTTP.UTF_8);
104 | return "Basic " + Base64.encodeToString(asBytes, Base64.NO_WRAP);
105 | } catch (UnsupportedEncodingException e) {
106 | log.e("Error encoding String as UTF-8 bytes: ", e);
107 | return "";
108 | }
109 | }
110 |
111 | static String readStream(InputStream in) throws IOException {
112 | StringBuilder strBuilder = new StringBuilder();
113 | try {
114 | BufferedReader reader = new BufferedReader(new InputStreamReader(in));
115 | for (String line = reader.readLine(); line != null; line = reader.readLine()) {
116 | strBuilder.append(line).append("\n");
117 | }
118 | return strBuilder.toString();
119 |
120 | } finally {
121 | in.close();
122 | }
123 | }
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/SparkCore/src/io/spark/core/android/cloud/requestservice/ClearableIntentService.java:
--------------------------------------------------------------------------------
1 | package io.spark.core.android.cloud.requestservice;
2 |
3 |
4 | /*
5 | * Copyright (C) 2008 The Android Open Source Project
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
8 | * use this file except in compliance with the License. You may obtain a copy of
9 | * the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16 | * License for the specific language governing permissions and limitations under
17 | * the License.
18 | */
19 |
20 |
21 | import android.app.Service;
22 | import android.content.Intent;
23 | import android.os.Handler;
24 | import android.os.HandlerThread;
25 | import android.os.IBinder;
26 | import android.os.Looper;
27 | import android.os.Message;
28 |
29 |
30 | /**
31 | * This is just a subclass of IntentService which can have its queue cleared
32 | * when by sending "ACTION_CLEAR_INTENT_QUEUE"
33 | *
34 | *
35 | * IntentService is a base class for {@link Service}s that handle asynchronous
36 | * requests (expressed as {@link Intent}s) on demand. Clients send requests
37 | * through {@link android.content.Context#startService(Intent)} calls; the
38 | * service is started as needed, handles each Intent in turn using a worker
39 | * thread, and stops itself when it runs out of work.
40 | *
41 | *
42 | * This "work queue processor" pattern is commonly used to offload tasks from an
43 | * application's main thread. The IntentService class exists to simplify this
44 | * pattern and take care of the mechanics. To use it, extend IntentService and
45 | * implement {@link #onHandleIntent(Intent)}. IntentService will receive the
46 | * Intents, launch a worker thread, and stop the service as appropriate.
47 | *
48 | *
49 | * All requests are handled on a single worker thread -- they may take as long
50 | * as necessary (and will not block the application's main loop), but only one
51 | * request will be processed at a time.
52 | *
53 | *
54 | *
Developer Guides
55 | *
56 | * For a detailed discussion about how to create services, read the Services
58 | * developer guide.
59 | *
60 | *
61 | *
62 | * @see android.os.AsyncTask
63 | */
64 | public abstract class ClearableIntentService extends Service {
65 |
66 |
67 | public static final String ACTION_CLEAR_INTENT_QUEUE = "ACTION_CLEAR_INTENT_QUEUE";
68 |
69 |
70 | private volatile Looper mServiceLooper;
71 | private volatile ServiceHandler mServiceHandler;
72 | private String mName;
73 | private boolean mRedelivery;
74 |
75 |
76 | private final class ServiceHandler extends Handler {
77 |
78 | public ServiceHandler(Looper looper) {
79 | super(looper);
80 | }
81 |
82 | @Override
83 | public void handleMessage(Message msg) {
84 | onHandleIntent((Intent) msg.obj);
85 | stopSelf(msg.arg1);
86 | }
87 | }
88 |
89 | /**
90 | * Creates an IntentService. Invoked by your subclass's constructor.
91 | *
92 | * @param name
93 | * Used to name the worker thread, important only for debugging.
94 | */
95 | public ClearableIntentService(String name) {
96 | super();
97 | mName = name;
98 | }
99 |
100 | /**
101 | * Sets intent redelivery preferences. Usually called from the constructor
102 | * with your preferred semantics.
103 | *
104 | *
105 | * If enabled is true, {@link #onStartCommand(Intent, int, int)} will return
106 | * {@link Service#START_REDELIVER_INTENT}, so if this process dies before
107 | * {@link #onHandleIntent(Intent)} returns, the process will be restarted
108 | * and the intent redelivered. If multiple Intents have been sent, only the
109 | * most recent one is guaranteed to be redelivered.
110 | *
111 | *
112 | * If enabled is false (the default),
113 | * {@link #onStartCommand(Intent, int, int)} will return
114 | * {@link Service#START_NOT_STICKY}, and if the process dies, the Intent
115 | * dies along with it.
116 | */
117 | public void setIntentRedelivery(boolean enabled) {
118 | mRedelivery = enabled;
119 | }
120 |
121 | @Override
122 | public void onCreate() {
123 | super.onCreate();
124 | HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
125 | thread.start();
126 |
127 | mServiceLooper = thread.getLooper();
128 | mServiceHandler = new ServiceHandler(mServiceLooper);
129 | }
130 |
131 | @Override
132 | public void onStart(Intent intent, int startId) {
133 | if (intent.getAction().equals(ACTION_CLEAR_INTENT_QUEUE)) {
134 | mServiceHandler.removeMessages(0);
135 | stopSelf(startId);
136 | } else {
137 | Message msg = mServiceHandler.obtainMessage();
138 | msg.arg1 = startId;
139 | msg.obj = intent;
140 | mServiceHandler.sendMessage(msg);
141 | }
142 | }
143 |
144 | /**
145 | * You should not override this method for your IntentService. Instead,
146 | * override {@link #onHandleIntent}, which the system calls when the
147 | * IntentService receives a start request.
148 | *
149 | * @see android.app.Service#onStartCommand
150 | */
151 | @Override
152 | public int onStartCommand(Intent intent, int flags, int startId) {
153 | onStart(intent, startId);
154 | return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
155 | }
156 |
157 | @Override
158 | public void onDestroy() {
159 | mServiceLooper.quit();
160 | }
161 |
162 | /**
163 | * Unless you provide binding for your service, you don't need to implement
164 | * this method, because the default implementation returns null.
165 | *
166 | * @see android.app.Service#onBind
167 | */
168 | @Override
169 | public IBinder onBind(Intent intent) {
170 | return null;
171 | }
172 |
173 | /**
174 | * This method is invoked on the worker thread with a request to process.
175 | * Only one Intent is processed at a time, but the processing happens on a
176 | * worker thread that runs independently from other application logic. So,
177 | * if this code takes a long time, it will hold up other requests to the
178 | * same IntentService, but it will not hold up anything else. When all
179 | * requests have been handled, the IntentService stops itself, so you should
180 | * not call {@link #stopSelf}.
181 | *
182 | * @param intent
183 | * The value passed to
184 | * {@link android.content.Context#startService(Intent)}.
185 | */
186 | protected abstract void onHandleIntent(Intent intent);
187 | }
188 |
--------------------------------------------------------------------------------
/SparkCore/src/io/spark/core/android/smartconfig/SmartConfigState.java:
--------------------------------------------------------------------------------
1 | package io.spark.core.android.smartconfig;
2 |
3 | import static org.solemnsilence.util.Py.set;
4 |
5 | import java.util.Set;
6 |
7 | import com.google.common.collect.Sets;
8 |
9 |
10 | public class SmartConfigState {
11 |
12 |
13 | private static final Set smartConfigFoundDeviceIds = set();
14 | private static final Set claimedButPossiblyUnnamedDeviceIds = set();
15 |
16 |
17 | public synchronized static Set getSmartConfigFoundDeviceIds() {
18 | return Sets.newHashSet(smartConfigFoundDeviceIds);
19 | }
20 |
21 | public synchronized static void addSmartConfigFoundId(String newId) {
22 | smartConfigFoundDeviceIds.add(newId);
23 | }
24 |
25 | public synchronized static void removeSmartConfigFoundDeviceId(String newId) {
26 | smartConfigFoundDeviceIds.remove(newId);
27 | }
28 |
29 | public synchronized static Set getClaimedButPossiblyUnnamedDeviceIds() {
30 | return Sets.newHashSet(claimedButPossiblyUnnamedDeviceIds);
31 | }
32 |
33 | public synchronized static void addClaimedButPossiblyUnnamedDeviceId(String newId) {
34 | claimedButPossiblyUnnamedDeviceIds.add(newId);
35 | }
36 |
37 | public synchronized static void removeClaimedButPossiblyUnnamedDeviceIds(String newId) {
38 | claimedButPossiblyUnnamedDeviceIds.remove(newId);
39 | }
40 |
41 | public synchronized static void clearSmartConfigData() {
42 | claimedButPossiblyUnnamedDeviceIds.clear();
43 | smartConfigFoundDeviceIds.clear();
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/SparkCore/src/io/spark/core/android/storage/Prefs.java:
--------------------------------------------------------------------------------
1 | package io.spark.core.android.storage;
2 |
3 | import static org.solemnsilence.util.Py.list;
4 | import io.spark.core.android.ui.tinker.PinAction;
5 |
6 | import java.util.List;
7 |
8 | import android.content.Context;
9 | import android.content.SharedPreferences;
10 |
11 |
12 | public class Prefs {
13 |
14 |
15 | private static final String BUCKET_NAME = "defaultPrefsBucket";
16 |
17 | private static final String KEY_USERNAME = "username";
18 | private static final String KEY_TOKEN = "token";
19 | private static final String KEY_COMPLETED_FIRST_LOGIN = "completedFirstLogin";
20 | private static final String KEY_CORES_JSON_ARRAY = "coresJsonArray";
21 | private static final String KEY_PIN_CONFIG_TEMPLATE = "corePinConfig_core-$1%s_pin-$2%s";
22 |
23 | private static Prefs instance = null;
24 |
25 | // making this a singleton to avoid having to pass around Context everywhere
26 | // that this info is needed. Kind of cheating, but in practice, it will be
27 | // fine here.
28 | public static Prefs getInstance() {
29 | return instance;
30 | }
31 |
32 | public static void initialize(Context ctx) {
33 | instance = new Prefs(ctx);
34 | }
35 |
36 |
37 | private final SharedPreferences prefs;
38 |
39 |
40 | private Prefs(Context context) {
41 | prefs = context.getApplicationContext()
42 | .getSharedPreferences(BUCKET_NAME, Context.MODE_PRIVATE);
43 | }
44 |
45 |
46 | public String getUsername() {
47 | return prefs.getString(KEY_USERNAME, null);
48 | }
49 |
50 | public void saveUsername(String username) {
51 | saveString(KEY_USERNAME, username);
52 | }
53 |
54 | public String getToken() {
55 | return prefs.getString(KEY_TOKEN, null);
56 | }
57 |
58 | public void saveToken(String token) {
59 | saveString(KEY_TOKEN, token);
60 | }
61 |
62 | public boolean getCompletedFirstLogin() {
63 | return prefs.getBoolean(KEY_COMPLETED_FIRST_LOGIN, false);
64 | }
65 |
66 | public void saveCompletedFirstLogin(boolean value) {
67 | prefs.edit().putBoolean(KEY_COMPLETED_FIRST_LOGIN, value).commit();
68 | }
69 |
70 | public String getCoresJsonArray() {
71 | return prefs.getString(KEY_CORES_JSON_ARRAY, "[]");
72 | }
73 |
74 | public void saveCoresJsonArray(String coresJson) {
75 | saveString(KEY_CORES_JSON_ARRAY, coresJson);
76 | }
77 |
78 |
79 | public PinAction getPinFunction(String coreId, String pinName) {
80 | String key = String.format(KEY_PIN_CONFIG_TEMPLATE, coreId, pinName);
81 | return PinAction.valueOf(
82 | prefs.getString(key, PinAction.NONE.name()));
83 | }
84 |
85 | public void savePinFunction(String coreId, String pinName, PinAction function) {
86 | String key = String.format(KEY_PIN_CONFIG_TEMPLATE, coreId, pinName);
87 | applyString(key, function.name());
88 | }
89 |
90 | public void clearTinker(String coreId) {
91 | List pinNames = list("A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "D0", "D1",
92 | "D2", "D3", "D4", "D5", "D6", "D7");
93 | for (String pinName : pinNames) {
94 | savePinFunction(coreId, pinName, PinAction.NONE);
95 | }
96 | }
97 |
98 | public void clear() {
99 | boolean completed = getCompletedFirstLogin();
100 | String username = getUsername();
101 | prefs.edit().clear().commit();
102 | saveCompletedFirstLogin(completed);
103 | saveUsername(username);
104 | }
105 |
106 | private void saveString(String key, String value) {
107 | prefs.edit().putString(key, value).commit();
108 | }
109 |
110 | private void applyString(String key, String value) {
111 | prefs.edit().putString(key, value).apply();
112 | }
113 |
114 | }
115 |
--------------------------------------------------------------------------------
/SparkCore/src/io/spark/core/android/storage/TinkerPrefs.java:
--------------------------------------------------------------------------------
1 | package io.spark.core.android.storage;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 |
6 |
7 | public class TinkerPrefs {
8 |
9 | private static final String BUCKET_NAME = "tinkerPrefsBucket";
10 |
11 | private static final String KEY_IS_VISITED = "isVisited";
12 |
13 | private static TinkerPrefs instance = null;
14 |
15 | // making this a singleton to avoid having to pass around Context everywhere
16 | // that this info is needed. Kind of cheating, but in practice, it will be
17 | // fine here.
18 | public static TinkerPrefs getInstance() {
19 | return instance;
20 | }
21 |
22 | public static void initialize(Context ctx) {
23 | instance = new TinkerPrefs(ctx);
24 | }
25 |
26 |
27 | private final SharedPreferences prefs;
28 |
29 |
30 | private TinkerPrefs(Context context) {
31 | prefs = context.getApplicationContext()
32 | .getSharedPreferences(BUCKET_NAME, Context.MODE_PRIVATE);
33 | }
34 |
35 | public boolean isFirstVisit() {
36 | return !prefs.getBoolean(KEY_IS_VISITED, false);
37 | }
38 |
39 | public void setVisited(boolean isVisited) {
40 | prefs.edit().putBoolean(KEY_IS_VISITED, isVisited).commit();
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/SparkCore/src/io/spark/core/android/ui/BaseFragment.java:
--------------------------------------------------------------------------------
1 | package io.spark.core.android.ui;
2 |
3 | import io.spark.core.android.cloud.ApiFacade;
4 | import io.spark.core.android.storage.Prefs;
5 | import io.spark.core.android.ui.util.Ui;
6 | import android.animation.Animator;
7 | import android.animation.AnimatorListenerAdapter;
8 | import android.app.Activity;
9 | import android.app.Fragment;
10 | import android.os.Bundle;
11 | import android.support.v4.content.LocalBroadcastManager;
12 | import android.view.LayoutInflater;
13 | import android.view.View;
14 | import android.view.ViewGroup;
15 |
16 |
17 | public abstract class BaseFragment extends Fragment {
18 |
19 | public abstract int getContentViewLayoutId();
20 |
21 |
22 | protected Prefs prefs;
23 | protected ApiFacade api;
24 | protected LocalBroadcastManager broadcastMgr;
25 |
26 |
27 | @Override
28 | public void onAttach(Activity activity) {
29 | super.onAttach(activity);
30 | prefs = Prefs.getInstance();
31 | api = ApiFacade.getInstance(activity);
32 | broadcastMgr = LocalBroadcastManager.getInstance(activity);
33 | }
34 |
35 | @Override
36 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
37 | return (ViewGroup) inflater.inflate(getContentViewLayoutId(), container, false);
38 | }
39 |
40 | /**
41 | * Shows & hides the progress spinner and hides the login form.
42 | */
43 | protected void showProgress(int viewId, final boolean show) {
44 | // Fade-in the progress spinner.
45 | int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime);
46 | final View progressView = Ui.findView(this, viewId);
47 | progressView.setVisibility(View.VISIBLE);
48 | progressView.animate()
49 | .setDuration(shortAnimTime)
50 | .alpha(show ? 1 : 0)
51 | .setListener(new AnimatorListenerAdapter() {
52 |
53 | @Override
54 | public void onAnimationEnd(Animator animation) {
55 | progressView.setVisibility(show ? View.VISIBLE : View.GONE);
56 | }
57 | });
58 | }
59 |
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/SparkCore/src/io/spark/core/android/ui/ErrorsDelegate.java:
--------------------------------------------------------------------------------
1 | package io.spark.core.android.ui;
2 |
3 | import io.spark.core.android.R;
4 | import io.spark.core.android.cloud.ApiFacade;
5 |
6 | import org.solemnsilence.util.TLog;
7 |
8 | import android.app.Activity;
9 | import android.app.AlertDialog;
10 | import android.content.BroadcastReceiver;
11 | import android.content.Context;
12 | import android.content.DialogInterface;
13 | import android.content.DialogInterface.OnClickListener;
14 | import android.content.Intent;
15 | import android.content.IntentFilter;
16 | import android.support.v4.content.LocalBroadcastManager;
17 |
18 |
19 | public class ErrorsDelegate {
20 |
21 | private static final TLog log = new TLog(ErrorsDelegate.class);
22 |
23 | private static final long MIN_DELAY_BETWEEN_DIALOGS_MILLIS = 10 * 1000;
24 |
25 |
26 | private final Activity activity;
27 | private final LocalBroadcastManager broadcastMgr;
28 | private final ErrorReceiver errorReceiver;
29 |
30 | private long lastShownUnreachableDialog = 0;
31 | private long lastShownHttpErrorDialog = 0;
32 | private long lastShownTinkerDialog = 0;
33 |
34 |
35 | public ErrorsDelegate(Activity activity) {
36 | this.activity = activity;
37 | this.errorReceiver = new ErrorReceiver();
38 | this.broadcastMgr = LocalBroadcastManager.getInstance(activity);
39 | }
40 |
41 | public void showCloudUnreachableDialog() {
42 | if (!canShowAnotherDialog(lastShownUnreachableDialog)) {
43 | log.d("Refusing to show another cloud unreachable dialog -- too soon since last one.");
44 | return;
45 | }
46 | lastShownUnreachableDialog = System.currentTimeMillis();
47 | showDialog(activity.getString(R.string.cloud_unreachable_msg));
48 | }
49 |
50 | public void showHttpErrorDialog(int statusCode) {
51 | if (!canShowAnotherDialog(lastShownHttpErrorDialog)) {
52 | log.d("Refusing to show another http error dialog -- too soon since last one.");
53 | return;
54 | }
55 | lastShownHttpErrorDialog = System.currentTimeMillis();
56 | showDialog(activity.getString(R.string.api_error_msg) + statusCode);
57 | }
58 |
59 | public void showTinkerError() {
60 | if (!canShowAnotherDialog(lastShownTinkerDialog)) {
61 | log.d("Refusing to show another tinker error dialog -- too soon since last one.");
62 | return;
63 | }
64 | lastShownTinkerDialog = System.currentTimeMillis();
65 | showDialog(activity.getString(R.string.tinker_error));
66 | }
67 |
68 |
69 | public void startListeningForErrors() {
70 | broadcastMgr.registerReceiver(errorReceiver, errorReceiver.getFilter());
71 | }
72 |
73 | public void stopListeningForErrors() {
74 | broadcastMgr.unregisterReceiver(errorReceiver);
75 | }
76 |
77 |
78 | private void showDialog(String message) {
79 | new AlertDialog.Builder(activity)
80 | .setMessage(message)
81 | .setPositiveButton(R.string.ok, new OnClickListener() {
82 |
83 | @Override
84 | public void onClick(DialogInterface dialog, int which) {
85 | dialog.dismiss();
86 | }
87 | })
88 | .create()
89 | .show();
90 | }
91 |
92 | private boolean canShowAnotherDialog(long lastShownTime) {
93 | return (System.currentTimeMillis() - MIN_DELAY_BETWEEN_DIALOGS_MILLIS > lastShownTime);
94 | }
95 |
96 |
97 | private class ErrorReceiver extends BroadcastReceiver {
98 |
99 | IntentFilter getFilter() {
100 | return new IntentFilter(ApiFacade.BROADCAST_SERVICE_API_ERROR);
101 | }
102 |
103 | @Override
104 | public void onReceive(Context context, Intent intent) {
105 | int errorCode = intent.getIntExtra(
106 | ApiFacade.EXTRA_ERROR_CODE, ApiFacade.REQUEST_FAILURE_CODE);
107 | if (errorCode == ApiFacade.REQUEST_FAILURE_CODE || errorCode < 300) {
108 | showCloudUnreachableDialog();
109 | } else {
110 | showHttpErrorDialog(errorCode);
111 | }
112 | }
113 | }
114 |
115 | }
116 |
--------------------------------------------------------------------------------
/SparkCore/src/io/spark/core/android/ui/assets/Typefaces.java:
--------------------------------------------------------------------------------
1 | package io.spark.core.android.ui.assets;
2 |
3 | import static org.solemnsilence.util.Py.map;
4 |
5 | import java.util.Map;
6 |
7 | import android.content.Context;
8 | import android.graphics.Typeface;
9 |
10 |
11 | public class Typefaces {
12 |
13 | // NOTE: this is tightly coupled to the filenames in assets/fonts
14 | public static enum Style {
15 | BOLD("gotham_bold.otf"),
16 | BOLD_ITALIC("gotham_bold_ita.otf"),
17 | BOOK("gotham_book.otf"),
18 | BOOK_ITALIC("gotham_book_ita.otf"),
19 | LIGHT("gotham_light.otf"),
20 | LIGHT_ITALIC("gotham_light_ita.otf"),
21 | MEDIUM("gotham_medium.otf"),
22 | MEDIUM_ITALIC("gotham_medium_ita.otf");
23 |
24 | public final String fileName;
25 |
26 | private Style(String name) {
27 | fileName = name;
28 | }
29 | }
30 |
31 |
32 | private static final Map