map = new HashMap<>();
24 | map.put(GithubApi.STATUS_UNAVAILABLE, R.string.error_server_unavailable_status);
25 | map.put(GithubApi.STATUS_GOOD, R.string.status_good);
26 | map.put(GithubApi.STATUS_MINOR, R.string.status_minor);
27 | map.put(GithubApi.STATUS_MAJOR, R.string.status_major);
28 | STATUS_MAP = Collections.unmodifiableMap(map);
29 | }
30 |
31 | public enum SpecialType {
32 | ERROR, LOADING
33 | }
34 |
35 | @SerializedName(STATUS)
36 | private String mStatus;
37 |
38 | @SerializedName(BODY)
39 | private String mBody;
40 |
41 | @SerializedName(CREATED_ON)
42 | private String mCreatedOn;
43 |
44 | private boolean mSpecial = false;
45 |
46 | public static Status getSpecialStatus(final Context context, final SpecialType type) {
47 |
48 | final Time now = new Time();
49 | now.setToNow();
50 |
51 | final Status specialStatus = new Status();
52 | specialStatus.mCreatedOn = now.format3339(false);
53 | specialStatus.mSpecial = true;
54 |
55 | switch (type) {
56 |
57 | case ERROR:
58 | specialStatus.mStatus = context.getString(R.string.error_server_unavailable_status);
59 | specialStatus.mBody = context.getString(R.string.error_server_unavailable_message);
60 | break;
61 |
62 | case LOADING:
63 | specialStatus.mStatus = context.getString(R.string.loading_status);
64 | specialStatus.mBody = context.getString(R.string.loading_message);
65 | break;
66 |
67 | }
68 |
69 | return specialStatus;
70 | }
71 |
72 | public Status() {
73 | }
74 |
75 | public boolean isSpecialStatus() {
76 | return mSpecial;
77 | }
78 |
79 | public String getStatus() {
80 | return mStatus == null ? "" : mStatus;
81 | }
82 |
83 | public String getBody() {
84 | return mBody == null ? "" : mBody;
85 | }
86 |
87 | public Level getLevel() {
88 | return Level.from(getStatus());
89 | }
90 |
91 | public static String getTranslatedStatus(final Context context, final Status status) {
92 |
93 | final String translatedStatus;
94 |
95 | if (status != null && status.getStatus() != null) {
96 | final String key = status.getStatus().toLowerCase();
97 | if (!STATUS_MAP.containsKey(key)) {
98 | // Fallback to default string
99 | translatedStatus = key;
100 |
101 | } else {
102 | final Integer statusResId = STATUS_MAP.get(key);
103 | translatedStatus = context.getString(statusResId);
104 | }
105 |
106 | } else {
107 |
108 | if (context != null) {
109 | translatedStatus = context.getString(R.string.error_server_unavailable_status);
110 |
111 | } else {
112 | translatedStatus = "Unavailable";
113 | }
114 |
115 | }
116 |
117 | return translatedStatus;
118 | }
119 |
120 | public Time getCreatedOn() {
121 | if (TextUtils.isEmpty(mCreatedOn)) {
122 | return null;
123 |
124 | } else {
125 | final Time time = new Time(Time.TIMEZONE_UTC);
126 | time.parse3339(mCreatedOn);
127 | time.switchTimezone(Time.getCurrentTimezone());
128 | return time;
129 | }
130 | }
131 |
132 | public static boolean shouldAlert(final Status oldStatus, final Status newStatus) {
133 |
134 | if (oldStatus == null) {
135 | // First request ever, no alert necessary
136 | return false;
137 |
138 | } else if (newStatus == null) {
139 | // Prevent a NullPointerException, but this *really* shouldn't happen
140 | return false;
141 |
142 | } else if (newStatus.getLevel().isHigherThan(Level.GOOD)) {
143 | // Any time time the status changes and it is above GOOD,
144 | // we should be alerting the change.
145 | // Clients may block it if the user decides to.
146 | return !Objects.equals(oldStatus, newStatus);
147 |
148 | } else {
149 | // Alert on a status level change
150 | return oldStatus.getLevel() != newStatus.getLevel();
151 | }
152 | }
153 |
154 | @Override
155 | public boolean equals(Object obj) {
156 |
157 | if (super.equals(obj)) {
158 | return true;
159 | }
160 |
161 | if (!(obj instanceof Status)) {
162 | return false;
163 | }
164 |
165 | final Status status = (Status) obj;
166 |
167 | if (!Objects.equals(getBody(), status.getBody())) {
168 | return false;
169 |
170 | } else if (!Objects.equals(getStatus(), status.getStatus())) {
171 | return false;
172 |
173 | } else if (!Objects.equals(getLevel(), status.getLevel())) {
174 | return false;
175 |
176 | } else if (!Objects.equals(getCreatedOn(), status.getCreatedOn())) {
177 | return false;
178 | }
179 |
180 | return true;
181 | }
182 |
183 | @Override
184 | public String toString() {
185 | return "Status{" +
186 | "mStatus='" + mStatus + '\'' +
187 | ", mBody='" + mBody + '\'' +
188 | ", mCreatedOn='" + mCreatedOn + '\'' +
189 | '}';
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/java/com/deange/githubstatus/push/BackoffHandler.java:
--------------------------------------------------------------------------------
1 | package com.deange.githubstatus.push;
2 |
3 | import android.os.Handler;
4 | import android.os.Looper;
5 |
6 | import java.util.Random;
7 |
8 | public abstract class BackoffHandler implements Runnable {
9 |
10 | private static final Handler sMainHandler = new Handler(Looper.getMainLooper());
11 | private static final Random sRandom = new Random();
12 |
13 | private final int mMaxTries;
14 | private final boolean mAsync;
15 | private boolean mCancel;
16 |
17 | public BackoffHandler(final int maxTries) {
18 | this(maxTries, false);
19 | }
20 |
21 | public BackoffHandler(final int maxTries, final boolean async) {
22 | mMaxTries = maxTries;
23 | mAsync = async;
24 | }
25 |
26 | public boolean isAsync() {
27 | return mAsync;
28 | }
29 |
30 | public void start() {
31 | if (mAsync) {
32 | new Thread(this).start();
33 | } else {
34 | run();
35 | }
36 | }
37 |
38 | public void cancel() {
39 | mCancel = true;
40 | }
41 |
42 | @Override
43 | public void run() {
44 |
45 | long maxDelay = 500L;
46 | int attempt = 0;
47 |
48 | for (;;) {
49 | boolean exit = false;
50 | try {
51 | exit = performAction();
52 | } catch (final Throwable ignored) {
53 | }
54 |
55 | if (exit) {
56 | // Successful
57 | break;
58 | }
59 |
60 | if (++attempt == mMaxTries) {
61 | // Unsuccessful
62 | break;
63 | }
64 |
65 | try {
66 | final long delay = sRandom.nextLong() % maxDelay;
67 | Thread.sleep(delay);
68 | } catch (final InterruptedException e) {
69 | throw new RuntimeException("This thread cannot be waited on!", e);
70 | }
71 |
72 | if (mCancel || Thread.currentThread().isInterrupted()) {
73 | // Action cancelled
74 | break;
75 | }
76 |
77 | maxDelay *= 2;
78 | }
79 |
80 | final int totalAttempts = attempt;
81 | sMainHandler.post(new Runnable() {
82 | @Override
83 | public void run() {
84 | onActionCompleted(!mCancel && totalAttempts != mMaxTries);
85 | }
86 | });
87 | }
88 |
89 | /**
90 | * @return true if this action completed successfully
91 | */
92 | public abstract boolean performAction() throws Throwable;
93 |
94 | /**
95 | * @param success true if this action completed successfully
96 | */
97 | public abstract void onActionCompleted(final boolean success);
98 | }
99 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/java/com/deange/githubstatus/push/OnPushMessageReceivedListener.java:
--------------------------------------------------------------------------------
1 | package com.deange.githubstatus.push;
2 |
3 | import android.content.Intent;
4 |
5 | public interface OnPushMessageReceivedListener {
6 | public void onPushMessageReceived(final Intent intent);
7 | }
8 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/java/com/deange/githubstatus/push/PushBaseActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.deange.githubstatus.push;
17 |
18 | import android.app.Dialog;
19 | import android.content.DialogInterface;
20 | import android.os.Bundle;
21 | import android.support.v4.app.FragmentActivity;
22 | import android.support.v7.app.ActionBarActivity;
23 | import android.util.Log;
24 |
25 | import com.deange.githubstatus.ui.TrackedActivity;
26 | import com.google.android.gms.common.ConnectionResult;
27 | import com.google.android.gms.common.GooglePlayServicesUtil;
28 |
29 | public abstract class PushBaseActivity
30 | extends TrackedActivity
31 | implements OnPushMessageReceivedListener {
32 |
33 | private static final String TAG = PushBaseActivity.class.getSimpleName();
34 |
35 | private final PushMessageReceiver mHandleGcmMessageReceiver = new PushMessageReceiver(this);
36 | private boolean mNeedToCheckPlayServices = true;
37 |
38 | @Override
39 | protected void onCreate(final Bundle savedInstanceState) {
40 | super.onCreate(savedInstanceState);
41 |
42 | // Listener for when a GCM message is received
43 | PushUtils.listenForGcmMessages(this, mHandleGcmMessageReceiver);
44 | }
45 |
46 | protected void registerIfNecessary() {
47 | final String regId = PushRegistrar.getRegistrationId(this);
48 | if (!PushRegistrar.isRegistered(this)) {
49 | PushRegistrar.register(this);
50 |
51 | } else if (!PushServerRegistrar.isRegisteredOnServer(this)) {
52 | PushServerRegistrar.register(PushBaseActivity.this, regId, true);
53 | }
54 | }
55 |
56 | protected void unregisterIfNecessary() {
57 | final String regId = PushRegistrar.getRegistrationId(this);
58 | if (!regId.isEmpty()) {
59 | // Device is already registered on GCM, check server.
60 | if (PushServerRegistrar.isRegisteredOnServer(this)) {
61 | // Device is registered on server, unregister them
62 | PushRegistrar.unregister(this);
63 | }
64 | }
65 | }
66 |
67 | protected boolean checkPlayServices() {
68 | return !mNeedToCheckPlayServices || performPlayServicesCheck();
69 | }
70 |
71 | private boolean performPlayServicesCheck() {
72 |
73 | mNeedToCheckPlayServices = false;
74 | final int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
75 | if (resultCode == ConnectionResult.SUCCESS) {
76 | return true;
77 |
78 | } else {
79 | Log.d(TAG, "isGooglePlayServicesAvailable = " + resultCode);
80 |
81 | if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
82 | final Dialog dialog = GooglePlayServicesUtil.getErrorDialog(resultCode, this, 0);
83 | dialog.setCancelable(false);
84 | dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
85 | @Override
86 | public void onDismiss(DialogInterface dialog) {
87 | mNeedToCheckPlayServices = true;
88 | }
89 | });
90 | dialog.show();
91 |
92 | } else {
93 | Log.e(TAG, "Unrecoverable error checking Google Play Services.");
94 | finish();
95 | }
96 |
97 | return false;
98 | }
99 | }
100 |
101 | @Override
102 | protected void onDestroy() {
103 | PushUtils.unregisterForGcmMessages(this, mHandleGcmMessageReceiver);
104 | super.onDestroy();
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/java/com/deange/githubstatus/push/PushBroadcastReceiver.java:
--------------------------------------------------------------------------------
1 | package com.deange.githubstatus.push;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.support.v4.content.WakefulBroadcastReceiver;
7 | import android.util.Log;
8 |
9 | public class PushBroadcastReceiver extends WakefulBroadcastReceiver {
10 |
11 | private static final String TAG = PushBroadcastReceiver.class.getSimpleName();
12 |
13 | @Override
14 | public final void onReceive(final Context context, final Intent intent) {
15 | Log.v(TAG, "onReceive(): " + intent.getAction());
16 |
17 | PushIntentService.runIntentInService(context, intent, PushIntentService.class.getName());
18 | setResult(Activity.RESULT_OK, null, null);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/java/com/deange/githubstatus/push/PushConstants.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.deange.githubstatus.push;
18 |
19 | /**
20 | * Constants used by the GCM library.
21 | */
22 | public final class PushConstants {
23 |
24 | private static final String C2DM_PACKAGE = "com.google.android.c2dm";
25 |
26 | public static final String INTENT_REGISTRATION_CALLBACK = build("REGISTRATION");
27 | public static final String INTENT_MESSAGE = build("RECEIVE");
28 |
29 | public static final String EXTRA_UNREGISTERED = "unregistered";
30 | public static final String EXTRA_ERROR = "error";
31 | public static final String EXTRA_REGISTRATION_ID = "registration_id";
32 | public static final String EXTRA_SPECIAL_MESSAGE = "message_type";
33 |
34 | private static String build(final String name) {
35 | return C2DM_PACKAGE + "." + "intent" + "." + name;
36 | }
37 |
38 | private PushConstants() {
39 | throw new UnsupportedOperationException();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/java/com/deange/githubstatus/push/PushIntentService.java:
--------------------------------------------------------------------------------
1 | package com.deange.githubstatus.push;
2 |
3 | import android.app.IntentService;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.os.PowerManager;
7 | import android.util.Log;
8 |
9 | import com.deange.githubstatus.Utils;
10 | import com.deange.githubstatus.controller.NotificationController;
11 | import com.deange.githubstatus.controller.StateController;
12 | import com.deange.githubstatus.http.GithubApi;
13 | import com.deange.githubstatus.model.Status;
14 | import com.deange.githubstatus.ui.TrackedActivity;
15 |
16 | import java.io.IOException;
17 |
18 | public class PushIntentService extends IntentService {
19 |
20 | private static final String TAG = PushIntentService.class.getSimpleName();
21 |
22 | private static final String WAKELOCK_KEY = Utils.buildAction("wakelock.key");
23 | private static final Object sLock = new Object();
24 |
25 | private static PowerManager.WakeLock sWakeLock;
26 |
27 | public PushIntentService() {
28 | super(TAG);
29 | }
30 |
31 | @Override
32 | protected void onHandleIntent(final Intent intent) {
33 |
34 | try {
35 | final Context context = getApplicationContext();
36 | final String action = intent.getAction();
37 |
38 | if (action == null) {
39 | Log.v(TAG, "Empty action from intent: \'" + intent + "\'");
40 |
41 | } else if (action.equals(PushConstants.INTENT_REGISTRATION_CALLBACK)) {
42 | handleRegistration(context, intent);
43 |
44 | } else if (action.equals(PushConstants.INTENT_MESSAGE)) {
45 | final String type = intent.getStringExtra(PushConstants.EXTRA_SPECIAL_MESSAGE);
46 | onMessage(context, intent, type);
47 | }
48 |
49 | } finally {
50 | synchronized (sLock) {
51 | if (sWakeLock != null && sWakeLock.isHeld()) {
52 | Log.v(TAG, "Releasing wakelock");
53 | sWakeLock.release();
54 | }
55 | }
56 | }
57 | }
58 |
59 | static void runIntentInService(final Context context, final Intent intent,
60 | final String className) {
61 | synchronized (sLock) {
62 | if (sWakeLock == null) {
63 | sWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE))
64 | .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
65 | }
66 | Log.v(TAG, "Acquiring wakelock");
67 | sWakeLock.acquire();
68 | }
69 |
70 | intent.setClassName(context, className);
71 | context.startService(intent);
72 | }
73 |
74 | private void handleRegistration(final Context context, Intent intent) {
75 | final String error = intent.getStringExtra(PushConstants.EXTRA_ERROR);
76 | final String registrationId = intent.getStringExtra(PushConstants.EXTRA_REGISTRATION_ID);
77 | final String unregistered = intent.getStringExtra(PushConstants.EXTRA_UNREGISTERED);
78 | Log.v(TAG, "handleRegistration: registrationId = " + registrationId +
79 | ", error = " + error + ", unregistered = " + unregistered);
80 |
81 | // registration succeeded
82 | if (registrationId != null) {
83 | PushRegistrar.setRegistrationId(context, registrationId);
84 | onRegistered(context, registrationId);
85 | return;
86 | }
87 |
88 | // unregistration succeeded
89 | if (unregistered != null) {
90 | final String oldRegistrationId = PushRegistrar.clearRegistrationId(context);
91 | onUnregistered(context, oldRegistrationId);
92 | return;
93 | }
94 |
95 | // last operation (registration or unregistration) returned an error;
96 | Log.v(TAG, "Registration error: " + error);
97 |
98 | // Registration failed
99 | onError(context, error);
100 |
101 | }
102 |
103 | public void onRegistered(final Context context, final String registrationId) {
104 |
105 | }
106 |
107 | public void onUnregistered(final Context context, final String oldRegistrationId) {
108 |
109 | }
110 |
111 | public void onMessage(final Context context, final Intent intent, final String type) {
112 |
113 | final Status newStatus;
114 | try {
115 | newStatus = GithubApi.getStatus(context);
116 |
117 | } catch (final IOException e) {
118 | // Cannot retrieve new status information
119 | return;
120 | }
121 |
122 | final Status oldStatus = StateController.getInstance().getStatus();
123 | final boolean alert = Status.shouldAlert(oldStatus, newStatus);
124 |
125 | // Save the new status
126 | StateController.getInstance().setStatus(newStatus);
127 |
128 | if (alert) {
129 | if (TrackedActivity.getVisibleActivities() == 0) {
130 | // Pop a notification that the status has now changed!
131 | NotificationController.getInstance().notificationForStatus(newStatus);
132 | }
133 | }
134 |
135 | // Let other active listeners know that we received a message
136 | final Intent broadcast = new Intent();
137 | broadcast.setAction(PushUtils.ACTION_GCM_MESSAGE_RECEIVED);
138 | context.sendBroadcast(broadcast);
139 | }
140 |
141 | public void onError(final Context context, final String error) {
142 |
143 | }
144 |
145 | }
146 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/java/com/deange/githubstatus/push/PushMessageReceiver.java:
--------------------------------------------------------------------------------
1 | package com.deange.githubstatus.push;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.util.Log;
7 |
8 | public class PushMessageReceiver
9 | extends BroadcastReceiver {
10 |
11 | private static final String TAG = PushMessageReceiver.class.getSimpleName();
12 |
13 | private final OnPushMessageReceivedListener mListener;
14 | private final String mAction;
15 |
16 | public PushMessageReceiver(final OnPushMessageReceivedListener listener) {
17 | mListener = listener;
18 | mAction = PushUtils.ACTION_GCM_MESSAGE_RECEIVED;
19 | }
20 |
21 | public String getAction() {
22 | return mAction;
23 | }
24 |
25 | @Override
26 | public void onReceive(final Context context, final Intent intent) {
27 |
28 | if (!mAction.equals(intent.getAction())) {
29 | return;
30 | }
31 |
32 | if (mListener == null) {
33 | Log.v(TAG, "mListener is null!");
34 |
35 | } else {
36 | mListener.onPushMessageReceived(intent);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/java/com/deange/githubstatus/push/PushRegistrar.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.deange.githubstatus.push;
18 |
19 | import android.content.Context;
20 | import android.content.SharedPreferences;
21 | import android.content.SharedPreferences.Editor;
22 | import android.text.TextUtils;
23 | import android.util.Log;
24 |
25 | import com.deange.githubstatus.BuildConfig;
26 | import com.deange.githubstatus.Utils;
27 | import com.google.android.gms.gcm.GoogleCloudMessaging;
28 |
29 | /**
30 | * Utilities for device registration.
31 | *
32 | * Note: this class uses a private {@link SharedPreferences}
33 | * object to keep track of the registration token.
34 | */
35 | public final class PushRegistrar {
36 |
37 | private static final String TAG = PushRegistrar.class.getSimpleName();
38 |
39 | private static final int DEFAULT_MAX_ATTEMPTS = 10;
40 | private static final String PREFERENCES = Utils.buildPreferences("registrar");
41 | private static final String PROPERTY_REG_ID = "regId";
42 | private static final String PROPERTY_APP_VERSION = "appVersion";
43 |
44 | private static PushRegisterTask sRegisterTask;
45 | private static PushUnregisterTask sUnregisterTask;
46 |
47 | public static void register(final Context context) {
48 | if (!isRegistered(context)) {
49 | Log.v(TAG, "Registering app " + context.getPackageName());
50 |
51 | if (sRegisterTask != null) {
52 | sRegisterTask.cancel();
53 | }
54 |
55 | sRegisterTask = new PushRegisterTask(context, DEFAULT_MAX_ATTEMPTS);
56 | sRegisterTask.start();
57 | }
58 | }
59 |
60 | public static void unregister(final Context context) {
61 | if (isRegistered(context)) {
62 | Log.v(TAG, "Unregistering app " + context.getPackageName());
63 |
64 | if (sUnregisterTask != null) {
65 | sUnregisterTask.cancel();
66 | }
67 |
68 | sUnregisterTask = new PushUnregisterTask(context, DEFAULT_MAX_ATTEMPTS);
69 | sUnregisterTask.start();
70 | }
71 | }
72 |
73 | public static String getRegistrationId(final Context context) {
74 | final SharedPreferences prefs = getGCMPreferences(context);
75 | String registrationId = prefs.getString(PROPERTY_REG_ID, "");
76 |
77 | // check if app was updated; if so, it must clear registration id to
78 | // avoid a race condition if GCM sends a message
79 | final int oldVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE);
80 | final int newVersion = BuildConfig.VERSION_CODE;
81 | if (oldVersion != Integer.MIN_VALUE && oldVersion != newVersion) {
82 | Log.v(TAG, "App version changed from " + oldVersion + " to " + newVersion
83 | + "; resetting registration id");
84 | clearRegistrationId(context);
85 | registrationId = "";
86 | }
87 |
88 | return registrationId;
89 | }
90 |
91 | public static boolean isRegistered(final Context context) {
92 | return !TextUtils.isEmpty(getRegistrationId(context));
93 | }
94 |
95 | static String clearRegistrationId(final Context context) {
96 | return setRegistrationId(context, "");
97 | }
98 |
99 | static String setRegistrationId(final Context context, final String regId) {
100 | final SharedPreferences prefs = getGCMPreferences(context);
101 | final String oldRegistrationId = prefs.getString(PROPERTY_REG_ID, "");
102 | final int appVersion = BuildConfig.VERSION_CODE;
103 |
104 | Log.v(TAG, "Saving regId on app version " + appVersion);
105 |
106 | final Editor editor = prefs.edit();
107 | editor.putString(PROPERTY_REG_ID, regId);
108 | editor.putInt(PROPERTY_APP_VERSION, appVersion);
109 | editor.apply();
110 |
111 | return oldRegistrationId;
112 | }
113 |
114 | private static SharedPreferences getGCMPreferences(final Context context) {
115 | return context.getApplicationContext()
116 | .getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE);
117 | }
118 |
119 | private PushRegistrar() {
120 | throw new UnsupportedOperationException();
121 | }
122 |
123 | public static class PushRegisterTask extends BackoffHandler {
124 |
125 | private final Context mContext;
126 | private final GoogleCloudMessaging mGcm;
127 | private String mRegistrationId = null;
128 |
129 | public PushRegisterTask(final Context context, final int maxTries) {
130 | super(maxTries, true);
131 | mContext = context.getApplicationContext();
132 | mGcm = GoogleCloudMessaging.getInstance(context);
133 | }
134 |
135 | @Override
136 | public boolean performAction() throws Throwable {
137 | mRegistrationId = mGcm.register(BuildConfig.SENDER_ID);
138 | PushServerRegistrar.register(mContext, mRegistrationId);
139 | return !TextUtils.isEmpty(mRegistrationId);
140 | }
141 |
142 | @Override
143 | public void onActionCompleted(final boolean success) {
144 | if (success) {
145 | setRegistrationId(mContext, mRegistrationId);
146 | }
147 | }
148 | }
149 |
150 | public static class PushUnregisterTask extends BackoffHandler {
151 |
152 | private final Context mContext;
153 | private final GoogleCloudMessaging mGcm;
154 |
155 | public PushUnregisterTask(final Context context, final int maxTries) {
156 | super(maxTries, true);
157 | mContext = context.getApplicationContext();
158 | mGcm = GoogleCloudMessaging.getInstance(context);
159 | }
160 |
161 | @Override
162 | public boolean performAction() throws Throwable {
163 | mGcm.unregister();
164 | PushServerRegistrar.unregister(mContext, getRegistrationId(mContext));
165 | return true;
166 | }
167 |
168 | @Override
169 | public void onActionCompleted(final boolean success) {
170 | if (success) {
171 | clearRegistrationId(mContext);
172 | }
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/java/com/deange/githubstatus/push/PushServerRegistrar.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.deange.githubstatus.push;
17 |
18 | import android.content.Context;
19 | import android.content.SharedPreferences;
20 | import android.util.Log;
21 |
22 | import com.deange.githubstatus.BuildConfig;
23 | import com.deange.githubstatus.Utils;
24 | import com.deange.githubstatus.http.HttpIOException;
25 |
26 | import java.io.IOException;
27 | import java.io.OutputStream;
28 | import java.net.HttpURLConnection;
29 | import java.net.MalformedURLException;
30 | import java.net.URL;
31 | import java.sql.Timestamp;
32 | import java.util.HashMap;
33 | import java.util.Iterator;
34 | import java.util.Map;
35 | import java.util.Map.Entry;
36 | import java.util.concurrent.TimeUnit;
37 |
38 | public final class PushServerRegistrar {
39 |
40 | private static final String TAG = PushServerRegistrar.class.getSimpleName();
41 | /**
42 | * Default lifespan (7 days) of the {@link PushServerRegistrar#isRegisteredOnServer(Context)}
43 | * flag until it is considered expired.
44 | */
45 | public static final long DEFAULT_ON_SERVER_LIFESPAN_MS = TimeUnit.DAYS.toMillis(7);
46 |
47 | private static final String PREFERENCES = Utils.buildPreferences("server.registrar");
48 | private static final String PROPERTY_ON_SERVER = "onServer";
49 | private static final String PROPERTY_ON_SERVER_EXPIRATION_TIME = "onServerExpirationTime";
50 |
51 | private static final String RELEASE_SERVER_URL = "http://githubstatus.appspot.com";
52 | private static final String SERVER_URL = BuildConfig.SERVER_URL;
53 | private static final int MAX_ATTEMPTS = 5;
54 |
55 | static void register(final Context context, final String regId) {
56 | register(context, regId, false);
57 | }
58 |
59 | static void register(final Context context, final String regId, final boolean async) {
60 | Log.i(TAG, "registering device (regId = " + regId + ")");
61 |
62 | final String serverUrl = SERVER_URL + "/register";
63 | final Map params = new HashMap<>();
64 | params.put("id", regId);
65 |
66 | Log.v(TAG, "Registering at '" + serverUrl + "'");
67 |
68 | final BackoffHandler task = new BackoffHandler(MAX_ATTEMPTS, async) {
69 | @Override
70 | public boolean performAction() throws Throwable {
71 | post(serverUrl, params);
72 | return true;
73 | }
74 |
75 | @Override
76 | public void onActionCompleted(final boolean success) {
77 | setRegisteredOnServer(context, success);
78 | }
79 | };
80 |
81 | task.start();
82 | }
83 |
84 | static void unregister(final Context context, final String regId) {
85 | Log.i(TAG, "unregistering device (regId = " + regId + ")");
86 |
87 | final String serverUrl = SERVER_URL + "/unregister";
88 | final Map params = new HashMap<>();
89 | params.put("id", regId);
90 |
91 | try {
92 | post(serverUrl, params);
93 |
94 | } catch (final IOException e) {
95 | // At this point the device is unregistered from GCM, but still
96 | // registered in the server.
97 | // We could try to unregister again, but it is not necessary:
98 | // if the server tries to send a message to the device, it will get
99 | // a "NotRegistered" error message and should unregister the device.
100 | } finally {
101 | setRegisteredOnServer(context, false);
102 | }
103 | }
104 |
105 | private static void post(final String endpoint, final Map params)
106 | throws IOException {
107 |
108 | final URL url;
109 | try {
110 | url = new URL(endpoint);
111 |
112 | } catch (final MalformedURLException e) {
113 | throw new IllegalArgumentException("invalid url: " + endpoint);
114 | }
115 |
116 | final StringBuilder bodyBuilder = new StringBuilder();
117 | final Iterator> iterator = params.entrySet().iterator();
118 |
119 | // constructs the POST body using the parameters
120 | while (iterator.hasNext()) {
121 | final Entry param = iterator.next();
122 | bodyBuilder.append(param.getKey()).append('=').append(param.getValue());
123 | if (iterator.hasNext()) {
124 | bodyBuilder.append('&');
125 | }
126 | }
127 |
128 | final String body = bodyBuilder.toString();
129 | final byte[] bytes = body.getBytes();
130 | Log.v(TAG, "Posting '" + body + "' to " + url);
131 | HttpURLConnection conn = null;
132 |
133 | try {
134 | conn = (HttpURLConnection) url.openConnection();
135 | conn.setDoOutput(true);
136 | conn.setUseCaches(false);
137 | conn.setFixedLengthStreamingMode(bytes.length);
138 | conn.setRequestMethod("POST");
139 | conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
140 |
141 | // post the request
142 | final OutputStream out = conn.getOutputStream();
143 | out.write(bytes);
144 | out.close();
145 |
146 | // handle the response
147 | final int status = conn.getResponseCode();
148 | if (status < 200 || status > 299) {
149 | throw new HttpIOException("Post failed with error code " + status, status);
150 | }
151 |
152 | } finally {
153 | if (conn != null) {
154 | conn.disconnect();
155 | }
156 | }
157 |
158 | }
159 |
160 | public static boolean isRegisteredOnServer(final Context context) {
161 | final SharedPreferences prefs = getGCMPreferences(context);
162 | final boolean isRegistered = prefs.getBoolean(PROPERTY_ON_SERVER, false);
163 | Log.v(TAG, "Is registered on server: " + isRegistered);
164 |
165 | if (isRegistered) {
166 | // checks if the information is not stale
167 | final long expirationTime = prefs.getLong(PROPERTY_ON_SERVER_EXPIRATION_TIME, -1);
168 | if (System.currentTimeMillis() > expirationTime) {
169 | Log.v(TAG, "flag expired on: " + new Timestamp(expirationTime));
170 | return false;
171 | }
172 | }
173 |
174 | return isRegistered;
175 | }
176 |
177 | public static void setRegisteredOnServer(final Context context, final boolean flag) {
178 | final SharedPreferences prefs = getGCMPreferences(context);
179 | final long lifespan = DEFAULT_ON_SERVER_LIFESPAN_MS;
180 | final long expirationTime = System.currentTimeMillis() + lifespan;
181 | Log.v(TAG, "Setting registeredOnServer status as " + flag + " until "
182 | + new Timestamp(expirationTime));
183 |
184 | final SharedPreferences.Editor editor = prefs.edit();
185 | editor.putBoolean(PROPERTY_ON_SERVER, flag);
186 | editor.putLong(PROPERTY_ON_SERVER_EXPIRATION_TIME, expirationTime);
187 | editor.apply();
188 | }
189 |
190 | private static SharedPreferences getGCMPreferences(final Context context) {
191 | return context.getApplicationContext()
192 | .getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE);
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/java/com/deange/githubstatus/push/PushUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.deange.githubstatus.push;
17 |
18 | import android.content.Context;
19 | import android.content.IntentFilter;
20 |
21 | import com.deange.githubstatus.Utils;
22 |
23 | public final class PushUtils {
24 |
25 | public static final String TAG = PushUtils.class.getSimpleName();
26 | static final String ACTION_GCM_MESSAGE_RECEIVED = Utils.buildAction("GCM_MESSAGE_RECEIVED");
27 |
28 | public static void listenForGcmMessages(
29 | final Context context,
30 | final PushMessageReceiver receiver) {
31 | context.registerReceiver(receiver, new IntentFilter(receiver.getAction()));
32 | }
33 |
34 | public static void unregisterForGcmMessages(
35 | final Context context,
36 | final PushMessageReceiver receiver) {
37 | context.unregisterReceiver(receiver);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/java/com/deange/githubstatus/ui/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.deange.githubstatus.ui;
2 |
3 | import android.annotation.TargetApi;
4 | import android.app.AlertDialog;
5 | import android.content.Intent;
6 | import android.net.Uri;
7 | import android.os.Build;
8 | import android.os.Bundle;
9 | import android.support.v4.app.FragmentTransaction;
10 | import android.support.v7.widget.Toolbar;
11 | import android.view.Menu;
12 | import android.view.MenuItem;
13 | import android.view.View;
14 | import android.widget.PopupMenu;
15 | import android.widget.TextView;
16 |
17 | import com.deange.githubstatus.PlatformUtils;
18 | import com.deange.githubstatus.R;
19 | import com.deange.githubstatus.Utils;
20 | import com.deange.githubstatus.model.SettingsInfo;
21 | import com.deange.githubstatus.push.PushBaseActivity;
22 | import com.melnykov.fab.FloatingActionButton;
23 |
24 | import java.util.Calendar;
25 |
26 | public class MainActivity
27 | extends PushBaseActivity
28 | implements
29 | View.OnClickListener,
30 | SettingsFragment.OnSettingsChangedListener,
31 | PopupMenu.OnMenuItemClickListener {
32 |
33 | private MainFragment mFragment;
34 |
35 | private static final String AVATAR_URL = "https://plus.google.com/+ChristianDeAngelis";
36 |
37 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
38 | @Override
39 | protected void onCreate(Bundle savedInstanceState) {
40 |
41 | if (Utils.showNiceView(this) && PlatformUtils.hasJellybean()) {
42 | getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
43 | }
44 |
45 | super.onCreate(savedInstanceState);
46 | setContentView(R.layout.activity_main);
47 |
48 | setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
49 | getSupportActionBar().setTitle(R.string.app_name);
50 |
51 | mFragment = (MainFragment) getSupportFragmentManager().findFragmentByTag(MainFragment.TAG);
52 | if (mFragment == null) {
53 | mFragment = MainFragment.newInstance();
54 | }
55 |
56 | if (!mFragment.isAdded()) {
57 | getSupportFragmentManager().beginTransaction().add(
58 | R.id.content_frame, mFragment, MainFragment.TAG).commit();
59 | }
60 |
61 | final FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.content_fab);
62 | if (fab != null) {
63 | fab.setOnClickListener(this);
64 | }
65 | }
66 |
67 | private void showInfoDialog() {
68 |
69 | final String developerName = getString(
70 | R.string.about_developer_name,
71 | Calendar.getInstance().get(Calendar.YEAR));
72 |
73 | final View dialogContentView = getLayoutInflater().inflate(R.layout.dialog_about, null);
74 | ((TextView) dialogContentView.findViewById(R.id.dialog_about_developer_name))
75 | .setText(developerName);
76 | dialogContentView.findViewById(R.id.dialog_about_avatar).setOnClickListener(this);
77 |
78 | new AlertDialog.Builder(this)
79 | .setView(dialogContentView)
80 | .show();
81 | }
82 |
83 | private void showOverflowMenu(final FloatingActionButton view) {
84 | final PopupMenu menu = new PopupMenu(this, view);
85 | menu.setOnMenuItemClickListener(this);
86 | menu.inflate(R.menu.activity_menu);
87 | menu.show();
88 | }
89 |
90 | private void showSettings() {
91 |
92 | if (!checkPlayServices()) {
93 | return;
94 | }
95 |
96 | final String tag = SettingsFragment.TAG;
97 | final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
98 | SettingsFragment fragment =
99 | (SettingsFragment) getSupportFragmentManager().findFragmentByTag(tag);
100 | if (fragment != null) {
101 | transaction.remove(fragment);
102 | }
103 |
104 | fragment = new SettingsFragment();
105 | fragment.show(transaction, tag);
106 | }
107 |
108 | @Override
109 | public boolean onCreateOptionsMenu(Menu menu) {
110 | getMenuInflater().inflate(R.menu.activity_menu, menu);
111 | return super.onCreateOptionsMenu(menu);
112 | }
113 |
114 | @Override
115 | public boolean onOptionsItemSelected(MenuItem item) {
116 |
117 | switch (item.getItemId()) {
118 | case R.id.menu_sync:
119 | refresh();
120 | return true;
121 |
122 | case R.id.menu_settings:
123 | showSettings();
124 | return true;
125 |
126 | case R.id.menu_info:
127 | showInfoDialog();
128 | return true;
129 |
130 | default:
131 | return super.onOptionsItemSelected(item);
132 |
133 | }
134 | }
135 |
136 | @Override
137 | public boolean onMenuItemClick(final MenuItem item) {
138 | // This is from the PopupMenu
139 | return onOptionsItemSelected(item);
140 | }
141 |
142 | private void refresh() {
143 | if (mFragment != null) {
144 | mFragment.refresh();
145 | }
146 | }
147 |
148 | @Override
149 | public void onPushMessageReceived(final Intent intent) {
150 | // Reload the fragment's content view
151 | refresh();
152 | }
153 |
154 | @Override
155 | public void onClick(final View v) {
156 |
157 | switch (v.getId()) {
158 | case R.id.content_fab:
159 | showOverflowMenu((FloatingActionButton) v);
160 | break;
161 |
162 | case R.id.dialog_about_avatar:
163 | startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse(AVATAR_URL)));
164 | break;
165 | }
166 | }
167 |
168 | @Override
169 | public void onSettingsChanged(final SettingsInfo settings) {
170 | if (settings.isGCMEnabled()) {
171 | // User is enabling push notifications
172 | registerIfNecessary();
173 |
174 | } else {
175 | // User is disabling push notifications
176 | unregisterIfNecessary();
177 | }
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/java/com/deange/githubstatus/ui/MainFragment.java:
--------------------------------------------------------------------------------
1 | package com.deange.githubstatus.ui;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.ArgbEvaluator;
6 | import android.animation.ObjectAnimator;
7 | import android.animation.ValueAnimator;
8 | import android.content.res.Configuration;
9 | import android.graphics.PorterDuff;
10 | import android.graphics.drawable.Drawable;
11 | import android.os.Bundle;
12 | import android.os.Handler;
13 | import android.support.v4.app.Fragment;
14 | import android.support.v4.widget.SwipeRefreshLayout;
15 | import android.util.Log;
16 | import android.view.LayoutInflater;
17 | import android.view.View;
18 | import android.view.ViewGroup;
19 | import android.widget.AbsListView;
20 | import android.widget.ListView;
21 | import android.widget.TextView;
22 | import android.widget.ViewSwitcher;
23 |
24 | import com.deange.githubstatus.R;
25 | import com.deange.githubstatus.Utils;
26 | import com.deange.githubstatus.controller.GsonController;
27 | import com.deange.githubstatus.http.GithubApi;
28 | import com.deange.githubstatus.http.HttpTask;
29 | import com.deange.githubstatus.model.Status;
30 | import com.deange.githubstatus.ui.view.SliceView;
31 |
32 | import java.util.ArrayList;
33 | import java.util.List;
34 | import java.util.concurrent.atomic.AtomicInteger;
35 |
36 | public class MainFragment
37 | extends Fragment
38 | implements
39 | SwipeRefreshLayout.OnRefreshListener,
40 | AbsListView.OnScrollListener,
41 | ViewSwitcher.ViewFactory {
42 |
43 | public static final String TAG = MainFragment.class.getSimpleName();
44 |
45 | private static final String KEY_STATUS = TAG + ".status";
46 | private static final long MINIMUM_UPDATE_DURATION = 1000;
47 | private static final int TOTAL_COMPONENTS = 2;
48 |
49 | private View mView;
50 | private SwipeRefreshLayout mSwipeLayout;
51 | private SliceView mSliceView;
52 | private TextView mLoadingView;
53 | private TextView mNothingView;
54 | private ListView mListView;
55 |
56 | private ViewSwitcher mStatusView;
57 | private Status mStatus;
58 |
59 | private List mMessages;
60 |
61 | private MessagesAdapter mAdapter;
62 | private final AtomicInteger mComponentsLoaded = new AtomicInteger();
63 | private final Handler mHandler = new Handler();
64 | private long mLastUpdate = 0;
65 | private ValueAnimator mAnimator;
66 | private int mColour;
67 |
68 | public static MainFragment newInstance() {
69 | return new MainFragment();
70 | }
71 |
72 | public MainFragment() {
73 | super();
74 | }
75 |
76 | @Override
77 | public void onCreate(final Bundle savedInstanceState) {
78 | Log.v(TAG, "onCreate()");
79 | super.onCreate(savedInstanceState);
80 |
81 | mAdapter = new MessagesAdapter(getActivity(), R.layout.list_item_message);
82 |
83 | if (savedInstanceState != null) {
84 | mStatus = GsonController.getInstance().fromJson(
85 | savedInstanceState.getString(KEY_STATUS, null), Status.class);
86 | }
87 | if (mStatus == null) {
88 | mStatus = Status.getSpecialStatus(getActivity(), Status.SpecialType.LOADING);
89 | }
90 | }
91 |
92 | @Override
93 | public View onCreateView(
94 | final LayoutInflater inflater,
95 | final ViewGroup container,
96 | final Bundle savedInstanceState) {
97 | Log.v(TAG, "onCreateView()");
98 |
99 | mView = inflater.inflate(R.layout.fragment_main, null);
100 |
101 | mStatusView = (ViewSwitcher) mView.findViewById(R.id.fragment_status_text_flipper);
102 | mStatusView.setFactory(this);
103 | mLoadingView = (TextView) mView.findViewById(R.id.loading_messages_view);
104 | mNothingView = (TextView) mView.findViewById(R.id.no_messages_view);
105 |
106 | mSwipeLayout = (SwipeRefreshLayout) mView.findViewById(R.id.fragment_swipe_container);
107 | mSwipeLayout.setOnRefreshListener(this);
108 | mSwipeLayout.setColorSchemeResources(R.color.status_good);
109 |
110 | mSliceView = (SliceView) mView.findViewById(R.id.fragment_slice_view);
111 |
112 | mListView = (ListView) mView.findViewById(R.id.fragment_messages_list_view);
113 | mListView.setDivider(null);
114 | mListView.setAdapter(mAdapter);
115 | mListView.setOnScrollListener(this);
116 |
117 | updateVisibility();
118 |
119 | return mView;
120 | }
121 |
122 | @Override
123 | public void onViewCreated(final View view, final Bundle savedInstanceState) {
124 | Log.v(TAG, "onViewCreated()");
125 | super.onViewCreated(view, savedInstanceState);
126 |
127 | if (mSliceView != null) {
128 | final double angle = Math.toDegrees(Math.atan2(
129 | mSliceView.getSliceHeight(),
130 | getResources().getDisplayMetrics().widthPixels));
131 | mStatusView.setRotation((float) angle);
132 |
133 | mStatusView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
134 | @Override
135 | public void onLayoutChange(
136 | final View v,
137 | final int left, final int top,
138 | final int right, final int bottom,
139 | final int oldLeft, final int oldTop,
140 | final int oldRight, final int oldBottom) {
141 |
142 | mStatusView.setPivotX(0);
143 | mStatusView.setPivotY(mStatusView.getMeasuredHeight());
144 | }
145 | });
146 | }
147 | }
148 |
149 | @Override
150 | public void onActivityCreated(final Bundle savedInstanceState) {
151 | Log.v(TAG, "onActivityCreated()");
152 | super.onActivityCreated(savedInstanceState);
153 |
154 | // Store this result ahead of time since the status and messages references
155 | // may point to intermediary (ie: pending) results
156 | final boolean shouldRefresh = (mStatus == null || mMessages == null);
157 |
158 | // Refresh status view
159 | mStatus = (mStatus == null)
160 | ? Status.getSpecialStatus(getActivity(), Status.SpecialType.LOADING)
161 | : mStatus;
162 |
163 | setStatus(mStatus);
164 |
165 | // Refresh messages view
166 | mMessages = (mMessages == null) ? new ArrayList() : mMessages;
167 | setMessages(mMessages);
168 |
169 | // Continue loading status info if necessary
170 | if (shouldRefresh) {
171 | refresh();
172 | }
173 |
174 | }
175 |
176 | @Override
177 | public void onResume() {
178 | Log.v(TAG, "onResume()");
179 | super.onResume();
180 | }
181 |
182 | @Override
183 | public void onPause() {
184 | Log.v(TAG, "onPause()");
185 | super.onPause();
186 | }
187 |
188 | @Override
189 | public void onSaveInstanceState(final Bundle outState) {
190 | outState.putString(KEY_STATUS, GsonController.getInstance().toJson(mStatus));
191 | super.onSaveInstanceState(outState);
192 | }
193 |
194 | @Override
195 | public void onRefresh() {
196 | // Called by SwipeRefreshLayout
197 | Log.v(TAG, "onRefresh()");
198 |
199 | refresh();
200 | }
201 |
202 | private void resetFieldsForRefresh() {
203 | mSwipeLayout.setRefreshing(true);
204 | mComponentsLoaded.set(0);
205 | mLastUpdate = System.currentTimeMillis();
206 | }
207 |
208 | public void refresh() {
209 |
210 | resetFieldsForRefresh();
211 |
212 | queryForStatus();
213 | queryForMessages();
214 | }
215 |
216 | private void queryForStatus() {
217 |
218 | GithubApi.getStatus(getActivity(), new HttpTask.Listener() {
219 | @Override
220 | public void onGet(final Status entity, final Exception exception) {
221 |
222 | mComponentsLoaded.incrementAndGet();
223 |
224 | final Status status = (exception == null)
225 | ? entity
226 | : Status.getSpecialStatus(getActivity(), Status.SpecialType.ERROR);
227 | setStatus(status);
228 | }
229 | });
230 | }
231 |
232 | private void queryForMessages() {
233 |
234 | GithubApi.getMessages(getActivity(), new HttpTask.Listener>() {
235 | @Override
236 | public void onGet(final List entity, final Exception exception) {
237 |
238 | mComponentsLoaded.incrementAndGet();
239 |
240 | final List statuses = (exception == null)
241 | ? entity
242 | : new ArrayList();
243 | setMessages(statuses);
244 | }
245 | });
246 | }
247 |
248 | private void updateVisibility() {
249 |
250 | final boolean allDataLoaded = mComponentsLoaded.get() == TOTAL_COMPONENTS;
251 |
252 | ViewUtils.setVisibility(mLoadingView, mMessages == null);
253 | ViewUtils.setVisibility(mNothingView, (mMessages == null || mMessages.isEmpty()));
254 |
255 | if (allDataLoaded) {
256 |
257 | // We want to keep the refresh UI up for *at least* MINIMUM_UPDATE_DURATION
258 | // Otherwise it looks very choppy and overall not a pleasant look
259 | final long now = System.currentTimeMillis();
260 | final long delay = MINIMUM_UPDATE_DURATION - (now - mLastUpdate);
261 | mLastUpdate = 0;
262 |
263 | mHandler.postDelayed(new Runnable() {
264 | @Override
265 | public void run() {
266 | mSwipeLayout.setRefreshing(false);
267 | }
268 | }, delay);
269 | }
270 | }
271 |
272 | @Override
273 | public void onScrollStateChanged(final AbsListView view, final int scrollState) {
274 | }
275 |
276 | @Override
277 | public void onScroll(
278 | final AbsListView view,
279 | final int firstVisibleItem,
280 | final int visibleItemCount,
281 | final int totalItemCount) {
282 |
283 | if (view == mListView) {
284 | int topRowVerticalPosition = (mListView == null || mListView.getChildCount() == 0)
285 | ? 0 : mListView.getChildAt(0).getTop();
286 | mSwipeLayout.setEnabled(firstVisibleItem == 0 && topRowVerticalPosition >= 0);
287 | }
288 | }
289 |
290 | private void setStatus(final Status status) {
291 | mStatus = (status == null)
292 | ? Status.getSpecialStatus(getActivity(), Status.SpecialType.ERROR)
293 | : status;
294 |
295 | mStatusView.setDisplayedChild(mStatusView.getDisplayedChild() == 0 ? 1 : 0);
296 | updateStatusView((TextView) mStatusView.getChildAt(mStatusView.getDisplayedChild()));
297 | updateVisibility();
298 | }
299 |
300 | private void setMessages(final List response) {
301 | mMessages = (response == null) ? new ArrayList() : response;
302 | mAdapter.refresh(mMessages);
303 | updateVisibility();
304 | }
305 |
306 | @Override
307 | public View makeView() {
308 | final TextView view = (TextView) LayoutInflater.from(getActivity()).inflate(
309 | R.layout.view_flipper_item, (ViewGroup) getView(), false);
310 | updateStatusView(view);
311 | return view;
312 | }
313 |
314 | private void animateColorFilter() {
315 | final int startColour = mColour;
316 | final int endColour = ViewUtils.resolveStatusColour(getActivity(), mStatus);
317 | final Drawable background = mView.getBackground();
318 |
319 | if (mAnimator != null) {
320 | mAnimator.cancel();
321 | }
322 |
323 | mAnimator = ObjectAnimator.ofInt(startColour, endColour);
324 | mAnimator.setEvaluator(new ArgbEvaluator());
325 | mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
326 | @Override
327 | public void onAnimationUpdate(final ValueAnimator animation) {
328 | mColour = (Integer) animation.getAnimatedValue();
329 | background.setColorFilter(mColour, PorterDuff.Mode.SRC_ATOP);
330 | }
331 | });
332 |
333 | mAnimator.addListener(new AnimatorListenerAdapter() {
334 | @Override
335 | public void onAnimationEnd(final Animator animation) {
336 | mAnimator = null;
337 | }
338 | });
339 |
340 | mAnimator.setDuration(getResources().getInteger(android.R.integer.config_mediumAnimTime));
341 | mAnimator.start();
342 | }
343 |
344 | private void updateStatusView(final TextView view) {
345 | if (Utils.showNiceView(getActivity())) {
346 | animateColorFilter();
347 |
348 | } else {
349 | view.setTextColor(ViewUtils.resolveStatusColour(getActivity(), mStatus));
350 | }
351 |
352 | view.setText(Status.getTranslatedStatus(getActivity(), mStatus).toUpperCase());
353 | }
354 | }
355 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/java/com/deange/githubstatus/ui/MessagesAdapter.java:
--------------------------------------------------------------------------------
1 | package com.deange.githubstatus.ui;
2 |
3 | import android.content.Context;
4 | import android.graphics.PorterDuff;
5 | import android.graphics.drawable.Drawable;
6 | import android.text.format.Time;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.ArrayAdapter;
11 | import android.widget.TextView;
12 |
13 | import com.deange.githubstatus.R;
14 | import com.deange.githubstatus.model.Status;
15 |
16 | import java.util.List;
17 |
18 | public class MessagesAdapter extends ArrayAdapter {
19 |
20 | public MessagesAdapter(Context context, int resource) {
21 | super(context, resource);
22 | }
23 |
24 | public void refresh(final List items) {
25 | clear();
26 |
27 | if (items != null) {
28 | for (Status status : items) {
29 | if (status != null) {
30 | add(status);
31 | }
32 | }
33 | }
34 |
35 | notifyDataSetChanged();
36 | }
37 |
38 | @Override
39 | public boolean isEnabled(int position) {
40 | return false;
41 | }
42 |
43 | @SuppressWarnings("deprecation")
44 | @Override
45 | public View getView(int position, final View convertView, ViewGroup parent) {
46 |
47 | final View view;
48 |
49 | if (convertView == null) {
50 | view = LayoutInflater.from(getContext()).inflate(R.layout.list_item_message, null);
51 | } else {
52 | view = convertView;
53 | }
54 |
55 | final Status status = getItem(position);
56 |
57 | ((TextView) view.findViewById(R.id.list_item_status)).setText(status.getBody());
58 |
59 | final Time messageTime = status.getCreatedOn();
60 | if (messageTime != null) {
61 | ((TextView) view.findViewById(R.id.list_item_timestamp)).setText(
62 | messageTime.format("%B %d %Y, %r"));
63 | }
64 |
65 | ViewUtils.setVisibility(view.findViewById(R.id.status_bar_top), position != 0);
66 | ViewUtils.setVisibility(view.findViewById(R.id.status_bar_bottom), position != getCount() - 1);
67 |
68 | final int statusColour = ViewUtils.resolveStatusColour(getContext(), status);
69 |
70 | final View statusIndicator = view.findViewById(R.id.status_indicator_circle);
71 | final Drawable drawable = statusIndicator.getBackground();
72 | drawable.mutate().setColorFilter(statusColour, PorterDuff.Mode.SRC_ATOP);
73 | statusIndicator.setBackgroundDrawable(drawable);
74 |
75 | return view;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/java/com/deange/githubstatus/ui/SettingsFragment.java:
--------------------------------------------------------------------------------
1 | package com.deange.githubstatus.ui;
2 |
3 | import android.app.Activity;
4 | import android.app.AlertDialog;
5 | import android.app.Dialog;
6 | import android.content.DialogInterface;
7 | import android.os.Bundle;
8 | import android.support.v4.app.DialogFragment;
9 | import android.view.View;
10 | import android.widget.Switch;
11 |
12 | import com.deange.githubstatus.R;
13 | import com.deange.githubstatus.model.SettingsInfo;
14 | import com.deange.githubstatus.push.PushServerRegistrar;
15 |
16 | public class SettingsFragment extends DialogFragment {
17 |
18 | public static final String TAG = SettingsFragment.class.getSimpleName();
19 |
20 | @Override
21 | public void onAttach(final Activity activity) {
22 | super.onAttach(activity);
23 | if (!(activity instanceof OnSettingsChangedListener)) {
24 | throw new IllegalStateException(
25 | "Activity must implement OnSettingsChangedListener!");
26 | }
27 | }
28 |
29 | @Override
30 | public Dialog onCreateDialog(final Bundle savedInstanceState) {
31 |
32 | final Activity activity = getActivity();
33 | final View root = View.inflate(activity, R.layout.fragment_settings, null);
34 | final Switch gcmSwitch = (Switch) root.findViewById(R.id.setting_gcm_switch);
35 |
36 | gcmSwitch.setChecked(PushServerRegistrar.isRegisteredOnServer(activity));
37 |
38 | return new AlertDialog.Builder(activity)
39 | .setView(root)
40 | .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
41 | @Override
42 | public void onClick(final DialogInterface dialog, final int which) {
43 | ((OnSettingsChangedListener) activity).onSettingsChanged(
44 | new SettingsInfo.Builder()
45 | .gcm(gcmSwitch.isChecked())
46 | .build()
47 | );
48 | }
49 | })
50 | .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
51 | @Override
52 | public void onClick(final DialogInterface dialog, final int which) {
53 | dismiss();
54 | }
55 | })
56 | .show();
57 | }
58 |
59 | public interface OnSettingsChangedListener {
60 | void onSettingsChanged(final SettingsInfo settings);
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/java/com/deange/githubstatus/ui/TrackedActivity.java:
--------------------------------------------------------------------------------
1 | package com.deange.githubstatus.ui;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.ActionBarActivity;
5 |
6 | public class TrackedActivity
7 | extends ActionBarActivity {
8 |
9 | private static int sActiveActivities = 0;
10 | private static int sVisibleActivities = 0;
11 |
12 | public static int getActiveActivities() {
13 | return sActiveActivities;
14 | }
15 |
16 | public static int getVisibleActivities() {
17 | return sVisibleActivities;
18 | }
19 |
20 | @Override
21 | protected void onCreate(final Bundle savedInstanceState) {
22 | sActiveActivities++;
23 | super.onCreate(savedInstanceState);
24 | }
25 |
26 | @Override
27 | protected void onResume() {
28 | sVisibleActivities++;
29 | super.onResume();
30 | }
31 |
32 | @Override
33 | protected void onPause() {
34 | super.onPause();
35 | sVisibleActivities--;
36 | }
37 |
38 | @Override
39 | protected void onDestroy() {
40 | super.onDestroy();
41 | sActiveActivities--;
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/java/com/deange/githubstatus/ui/ViewUtils.java:
--------------------------------------------------------------------------------
1 | package com.deange.githubstatus.ui;
2 |
3 | import android.content.Context;
4 | import android.content.res.Resources;
5 | import android.graphics.Color;
6 | import android.view.View;
7 |
8 | import com.deange.githubstatus.R;
9 | import com.deange.githubstatus.http.GithubApi;
10 | import com.deange.githubstatus.model.Status;
11 |
12 | public final class ViewUtils {
13 |
14 | public static int resolveStatusColour(final Context context, final Status status) {
15 |
16 | if (context == null) {
17 | return Color.BLACK;
18 | }
19 |
20 | if (status == null || status.getStatus() == null) {
21 | return context.getResources().getColor(R.color.status_major);
22 | }
23 |
24 | final int colourResId;
25 | final Resources res = context.getResources();
26 | final String statusString = status.getStatus();
27 |
28 | if (GithubApi.STATUS_GOOD.equalsIgnoreCase(statusString)) {
29 | colourResId = R.color.status_good;
30 |
31 | } else if (GithubApi.STATUS_MINOR.equalsIgnoreCase(statusString)) {
32 | colourResId = R.color.status_minor;
33 |
34 | } else if (GithubApi.STATUS_MAJOR.equalsIgnoreCase(statusString)) {
35 | colourResId = R.color.status_major;
36 |
37 | } else {
38 | colourResId = android.R.color.black;
39 | }
40 |
41 | return res.getColor(colourResId);
42 |
43 | }
44 |
45 | public static void setVisibility(final View view, final boolean visibility) {
46 | view.setVisibility(visibility ? View.VISIBLE : View.GONE);
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/java/com/deange/githubstatus/ui/view/AutoScaleTextView.java:
--------------------------------------------------------------------------------
1 | package com.deange.githubstatus.ui.view;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.text.Layout;
6 | import android.text.StaticLayout;
7 | import android.text.TextPaint;
8 | import android.text.TextUtils;
9 | import android.util.AttributeSet;
10 | import android.util.TypedValue;
11 |
12 | import com.deange.githubstatus.R;
13 |
14 | public class AutoScaleTextView extends FontTextView {
15 |
16 | private float mScaleFactor;
17 | private float mMinTextSize;
18 | private float mMaxTextSize;
19 |
20 | private static final float DEFAULT_SCALED_FACTOR = 1f;
21 | private static final float DEFAULT_MIN_TEXT_SIZE = 10f;
22 | private static final float DEFAULT_MAX_TEXT_SIZE = 256f;
23 | private static final int DEFAULT_LINE_COUNT = 1;
24 |
25 | public AutoScaleTextView(final Context context) {
26 | this(context, null);
27 | }
28 |
29 | public AutoScaleTextView(final Context context, final AttributeSet attrs) {
30 | this(context, attrs, 0);
31 | }
32 |
33 | public AutoScaleTextView(final Context context, final AttributeSet attrs, final int defStyle) {
34 | super(context, attrs, defStyle);
35 |
36 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AutoScaleTextView, defStyle, 0);
37 |
38 | mScaleFactor = DEFAULT_SCALED_FACTOR;
39 | mMinTextSize = DEFAULT_MIN_TEXT_SIZE;
40 | mMaxTextSize = DEFAULT_MAX_TEXT_SIZE;
41 |
42 | if (a != null) {
43 | mScaleFactor = a.getFraction(R.styleable.AutoScaleTextView_scaleFactor, 1, 1, DEFAULT_SCALED_FACTOR);
44 | mMinTextSize = a.getDimension(R.styleable.AutoScaleTextView_minTextSize, DEFAULT_MIN_TEXT_SIZE);
45 | mMaxTextSize = a.getDimension(R.styleable.AutoScaleTextView_maxTextSize, DEFAULT_MAX_TEXT_SIZE);
46 |
47 | a.recycle();
48 | }
49 |
50 | }
51 |
52 | private void rescaleText() {
53 |
54 | if (TextUtils.isEmpty(getText())) {
55 | return;
56 | }
57 |
58 | float size = mMinTextSize;
59 |
60 | final TextPaint paint = new TextPaint(getPaint());
61 | paint.setTextSize(size);
62 |
63 | // Use modified gallop search to converge to an appropriate text size
64 | while (getLineCount(paint) <= DEFAULT_LINE_COUNT) {
65 | if (size >= mMaxTextSize) break;
66 | size *= 2;
67 | paint.setTextSize(size);
68 | }
69 |
70 | while (getLineCount(paint) > DEFAULT_LINE_COUNT) {
71 | if (size <= mMinTextSize) break;
72 | size -= 1;
73 | paint.setTextSize(size);
74 | }
75 |
76 | size *= mScaleFactor;
77 |
78 | // Renormalize
79 | size = Math.min(size, mMaxTextSize);
80 | size = Math.max(size, mMinTextSize);
81 |
82 | setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
83 | }
84 |
85 | public void setScaleFactor(final float scaleFactor) {
86 | mScaleFactor = scaleFactor;
87 | }
88 |
89 | public float getScaleFactor() {
90 | return mScaleFactor;
91 | }
92 |
93 | public int getLineCount(final TextPaint paint) {
94 | final String text = getText().toString();
95 | final int targetWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
96 | return new StaticLayout(text, paint, targetWidth,
97 | Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true).getLineCount();
98 | }
99 |
100 | @Override
101 | protected void onLayout(final boolean changed, final int left, final int top, final int right, final int bottom) {
102 | super.onLayout(changed, left, top, right, bottom); // Does stuff for scroller and editor
103 | if (changed) {
104 | rescaleText();
105 | }
106 | }
107 |
108 | @Override
109 | protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) {
110 | rescaleText();
111 | }
112 |
113 | }
--------------------------------------------------------------------------------
/GithubStatus/src/main/java/com/deange/githubstatus/ui/view/FontTextView.java:
--------------------------------------------------------------------------------
1 | package com.deange.githubstatus.ui.view;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Typeface;
6 | import android.text.TextUtils;
7 | import android.util.AttributeSet;
8 | import android.widget.TextView;
9 |
10 | import com.deange.githubstatus.R;
11 |
12 | public class FontTextView extends TextView {
13 |
14 | public FontTextView(final Context context) {
15 | this(context, null);
16 | }
17 |
18 | public FontTextView(final Context context, final AttributeSet attrs) {
19 | this(context, attrs, 0);
20 | }
21 |
22 | public FontTextView(final Context context, final AttributeSet attrs, final int defStyle) {
23 | super(context, attrs, defStyle);
24 |
25 | final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FontTextView, defStyle, 0);
26 |
27 | String fontName = null;
28 | if (a != null) {
29 | fontName = a.getString(R.styleable.FontTextView_fontName);
30 | a.recycle();
31 | }
32 |
33 | if (isInEditMode()) {
34 | // Fix to view the TextFontView in resource previewer
35 | return;
36 | }
37 |
38 | if (!TextUtils.isEmpty(fontName)) {
39 | setTypeface(Typeface.createFromAsset(getContext().getAssets(), fontName));
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/java/com/deange/githubstatus/ui/view/SelectableRoundedImageView.java:
--------------------------------------------------------------------------------
1 | package com.deange.githubstatus.ui.view;
2 |
3 | import android.content.Context;
4 | import android.graphics.Rect;
5 | import android.util.AttributeSet;
6 | import android.view.MotionEvent;
7 |
8 | import com.makeramen.RoundedImageView;
9 |
10 | public class SelectableRoundedImageView extends RoundedImageView {
11 |
12 | private int mSelectedColour;
13 | private int mNormalColour;
14 | private final Rect mBounds = new Rect();
15 |
16 | public SelectableRoundedImageView(final Context context) {
17 | super(context);
18 | getColours();
19 | }
20 |
21 | public SelectableRoundedImageView(final Context context, final AttributeSet attrs) {
22 | super(context, attrs);
23 | getColours();
24 | }
25 |
26 | public SelectableRoundedImageView(final Context context, final AttributeSet attrs, final int defStyle) {
27 | super(context, attrs, defStyle);
28 | getColours();
29 | }
30 |
31 | private void getColours() {
32 | mSelectedColour = getResources().getColor(android.R.color.holo_blue_light);
33 | mNormalColour = getBorderColor();
34 | }
35 |
36 | @Override
37 | public void setBorderColor(final int color) {
38 | mNormalColour = getBorderColor();
39 | super.setBorderColor(color);
40 | }
41 |
42 | @Override
43 | public boolean onTouchEvent(final MotionEvent event) {
44 |
45 | final boolean inBounds = isInEllipseBounds(event.getX(), event.getY());
46 |
47 | switch (event.getAction()) {
48 |
49 | case MotionEvent.ACTION_DOWN:
50 | if (inBounds) {
51 | super.setBorderColor(mSelectedColour);
52 | } else {
53 | return true;
54 | }
55 | break;
56 |
57 | case MotionEvent.ACTION_MOVE:
58 | if (!inBounds) {
59 | super.setBorderColor(mNormalColour);
60 | return true;
61 | }
62 | break;
63 |
64 | case MotionEvent.ACTION_CANCEL:
65 | case MotionEvent.ACTION_UP:
66 | super.setBorderColor(mNormalColour);
67 | if (!inBounds) {
68 | return true;
69 | }
70 | break;
71 | }
72 |
73 | return super.onTouchEvent(event);
74 | }
75 |
76 | private boolean isInEllipseBounds(final float x, final float y) {
77 |
78 | final float a = getWidth() / 2;
79 | final float b = getHeight() / 2;
80 | final float radius = Math.min(a, b);
81 |
82 | return Math.pow((x - a) / radius, 2) + Math.pow((y - b) / radius, 2) <= 1;
83 | }
84 |
85 | @Override
86 | protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
87 | mBounds.set(0, 0, MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));
88 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/java/com/deange/githubstatus/ui/view/SliceView.java:
--------------------------------------------------------------------------------
1 | package com.deange.githubstatus.ui.view;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.content.res.TypedArray;
6 | import android.graphics.Canvas;
7 | import android.graphics.Outline;
8 | import android.graphics.Path;
9 | import android.graphics.Region;
10 | import android.os.Build;
11 | import android.util.AttributeSet;
12 | import android.util.TypedValue;
13 | import android.view.View;
14 | import android.view.ViewOutlineProvider;
15 | import android.widget.RelativeLayout;
16 |
17 | import com.deange.githubstatus.R;
18 |
19 | public class SliceView extends RelativeLayout {
20 |
21 | private static final float DEFAULT_HEIGHT = 100;
22 | private static final float DEFAULT_OFFSET = 0;
23 |
24 | private final Path mPath = new Path();
25 |
26 | private float mSliceOffset;
27 | private float mSliceHeight;
28 |
29 | public SliceView(final Context context) {
30 | this(context, null);
31 | }
32 |
33 | public SliceView(final Context context, final AttributeSet attrs) {
34 | this(context, attrs, 0);
35 | }
36 |
37 | public SliceView(final Context context, final AttributeSet attrs, final int defStyle) {
38 | super(context, attrs, defStyle);
39 |
40 | final TypedArray a =
41 | getContext().obtainStyledAttributes(attrs, R.styleable.SliceView, defStyle, 0);
42 | if (a != null) {
43 | mSliceHeight = a.getDimensionPixelSize(
44 | R.styleable.SliceView_sliceHeight, (int) DEFAULT_HEIGHT);
45 | mSliceOffset = a.getDimensionPixelSize(
46 | R.styleable.SliceView_sliceOffset, (int) DEFAULT_OFFSET);
47 | a.recycle();
48 |
49 | } else {
50 | mSliceHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_HEIGHT,
51 | getResources().getDisplayMetrics());
52 | mSliceOffset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_OFFSET,
53 | getResources().getDisplayMetrics());
54 | }
55 |
56 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
57 | setOutlineProvider(new ViewOutlineProvider() {
58 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
59 | @Override
60 | public void getOutline(final View view, final Outline outline) {
61 | outline.setConvexPath(mPath);
62 | }
63 | });
64 | }
65 |
66 | }
67 |
68 | public float getSliceOffset() {
69 | return mSliceOffset;
70 | }
71 |
72 | public void setSliceOffset(final int slicePixelsOffset) {
73 | mSliceOffset = slicePixelsOffset;
74 | requestLayout();
75 | invalidate();
76 | }
77 |
78 | public float getSliceHeight() {
79 | return mSliceHeight;
80 | }
81 |
82 | public void setSliceHeight(final int slicePixelsHeight) {
83 | mSliceHeight = slicePixelsHeight;
84 | requestLayout();
85 | invalidate();
86 | }
87 |
88 | @Override
89 | protected void onLayout(final boolean changed,
90 | final int l, final int t, final int r, final int b) {
91 | super.onLayout(changed, l, t, r, b);
92 |
93 | mPath.reset();
94 | mPath.moveTo(0 , mSliceOffset);
95 | mPath.lineTo(getMeasuredWidth(), mSliceOffset + mSliceHeight);
96 | mPath.lineTo(getMeasuredWidth(), getMeasuredHeight());
97 | mPath.lineTo(0 , getMeasuredHeight());
98 | mPath.lineTo(0 , mSliceOffset);
99 |
100 |
101 | }
102 |
103 | @Override
104 | public void draw(final Canvas canvas) {
105 | canvas.clipPath(mPath, Region.Op.INTERSECT);
106 | super.draw(canvas);
107 | }
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-hdpi/ic_action_info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-hdpi/ic_action_info.png
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-hdpi/ic_action_overflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-hdpi/ic_action_overflow.png
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-hdpi/ic_stat_octocat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-hdpi/ic_stat_octocat.png
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-mdpi/ic_action_info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-mdpi/ic_action_info.png
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-mdpi/ic_action_overflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-mdpi/ic_action_overflow.png
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-mdpi/ic_stat_octocat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-mdpi/ic_stat_octocat.png
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-nodpi/bkg_menu_banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-nodpi/bkg_menu_banner.png
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-nodpi/octocat_notif_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-nodpi/octocat_notif_green.png
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-nodpi/octocat_notif_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-nodpi/octocat_notif_red.png
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-nodpi/octocat_notif_yellow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-nodpi/octocat_notif_yellow.png
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-nodpi/thumbnail.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-nodpi/thumbnail.jpg
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-xhdpi/ic_action_info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-xhdpi/ic_action_info.png
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-xhdpi/ic_action_overflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-xhdpi/ic_action_overflow.png
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-xhdpi/ic_stat_octocat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-xhdpi/ic_stat_octocat.png
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-xxhdpi/ic_action_info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-xxhdpi/ic_action_info.png
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-xxhdpi/ic_action_overflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-xxhdpi/ic_action_overflow.png
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable-xxhdpi/ic_stat_octocat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/GithubStatus/src/main/res/drawable-xxhdpi/ic_stat_octocat.png
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable/background_tile.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/drawable/message_status_indicator.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/layout-land/activity_main.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
12 |
13 |
18 |
19 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/layout-land/fragment_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
13 |
14 |
19 |
20 |
26 |
27 |
35 |
36 |
44 |
45 |
46 |
47 |
48 |
55 |
56 |
57 |
69 |
70 |
76 |
77 |
85 |
86 |
95 |
96 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/layout-land/view_flipper_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/layout/activity_gcm.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
20 |
21 |
25 |
26 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
15 |
28 |
29 |
34 |
35 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/layout/dialog_about.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
15 |
16 |
26 |
27 |
33 |
34 |
41 |
42 |
49 |
50 |
51 |
52 |
53 |
54 |
62 |
63 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/layout/fragment_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
15 |
16 |
20 |
21 |
30 |
31 |
42 |
43 |
56 |
57 |
63 |
64 |
72 |
73 |
82 |
83 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/layout/fragment_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
20 |
21 |
32 |
33 |
42 |
43 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/layout/list_item_message.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
12 |
13 |
22 |
23 |
32 |
33 |
39 |
40 |
41 |
42 |
47 |
48 |
49 |
55 |
56 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/layout/view_flipper_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/menu/activity_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/menu/options_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/values-land-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/values-land/bools.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/values-v19/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/values/bools.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/values/colours.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #FF339966
5 | #FFF29D50
6 | #FFCC3300
7 |
8 | @color/status_good
9 | @color/status_good
10 |
11 | #FF123456
12 | #FFEEEEEE
13 |
14 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 16dp
3 | 16dp
4 |
5 | 75dp
6 |
7 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | GitHub Status
5 | Settings
6 |
7 | Unavailable
8 | Something went wrong.
9 | Loading\u2026
10 | Previous Messages
11 | Please be patient.
12 | No messages found.
13 |
14 | GitHub is currently
15 | Refresh
16 | Settings
17 | Info
18 | © Christian De Angelis
19 | © Christian De Angelis, %s
20 | This app is not affiliated with github.com
21 |
22 | Good
23 | Minor
24 | Major
25 |
26 | Send
27 | Clear
28 |
29 | Roboto-Black.ttf
30 | Roboto-Bold.ttf
31 | Roboto-Light.ttf
32 | Roboto-LightItalic.ttf
33 | Roboto-Thin.ttf
34 | Roboto-ThinItalic.ttf
35 |
36 |
37 |
38 | Register
39 | Unregister
40 | Clear
41 |
42 | Push Notifications
43 | Receive notifications when GitHub\'s status changes.
44 |
45 |
46 |
--------------------------------------------------------------------------------
/GithubStatus/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
8 |
9 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/LICENSE
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | DEPRECATED
2 | ==========
3 |
4 | **GitHub Status is now Hubbub! Check it out – https://github.com/cdeange/hubbub**
5 |
6 |
7 |
8 | # github-status
9 |
10 | Simple app to connect to the GitHub Status API. [Download the app!][1]
11 |
12 | ---
13 | ### What is it?
14 | GitHub-Status keeps you up-to-date with the most recent status of GitHub, built on top of their [Status API][2].
15 |
16 | ---
17 | ### Dependencies
18 | GitHub Status uses the **Android Support Library** for the provided components, even though it only supports ICS and above.
19 | It also leverages **Gson** and **OkHttp** for network connections and decoding JSON from the Status API.
20 |
21 | GitHub Status supports API level 14 (Ice Cream Sandwich) and above.
22 |
23 | ---
24 | ### Contributing
25 | Just submit a pull request if you think you have a cool idea for a new fix or feature!
26 | If you're planning on checking out the repo, please respect the file structure and work on it using Android Studio.
27 |
28 | ---
29 | ### Developed By
30 | - Christian De Angelis -
31 |
32 | ---
33 | ### License
34 | ```
35 | Copyright 2018 Christian De Angelis
36 |
37 | Licensed under the Apache License, Version 2.0 (the "License");
38 | you may not use this file except in compliance with the License.
39 | You may obtain a copy of the License at
40 |
41 | http://www.apache.org/licenses/LICENSE-2.0
42 |
43 | Unless required by applicable law or agreed to in writing, software
44 | distributed under the License is distributed on an "AS IS" BASIS,
45 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
46 | See the License for the specific language governing permissions and
47 | limitations under the License.
48 | ```
49 |
50 | [1]: https://play.google.com/store/apps/details?id=com.deange.githubstatus
51 | [2]: https://status.github.com
52 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christiandeange/github-status/4d84de3436eff5ab882a7109e0e7074a1a5757e8/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Jan 11 16:58:17 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':GithubStatus'
2 |
--------------------------------------------------------------------------------