... | ... |
@@ -0,0 +1 @@ |
1 |
+/build |
... | ... |
@@ -0,0 +1,35 @@ |
1 |
+apply plugin: 'com.android.application' |
|
2 |
+ |
|
3 |
+android { |
|
4 |
+ compileSdkVersion 26 |
|
5 |
+ buildToolsVersion "26.0.0" |
|
6 |
+ defaultConfig { |
|
7 |
+ applicationId "app.librenews.io.librenews" |
|
8 |
+ minSdkVersion 16 |
|
9 |
+ targetSdkVersion 26 |
|
10 |
+ versionCode 1 |
|
11 |
+ versionName "1.0" |
|
12 |
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" |
|
13 |
+ vectorDrawables.useSupportLibrary = true |
|
14 |
+ } |
|
15 |
+ buildTypes { |
|
16 |
+ release { |
|
17 |
+ minifyEnabled false |
|
18 |
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' |
|
19 |
+ } |
|
20 |
+ } |
|
21 |
+} |
|
22 |
+ |
|
23 |
+dependencies { |
|
24 |
+ compile fileTree(dir: 'libs', include: ['*.jar']) |
|
25 |
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { |
|
26 |
+ exclude group: 'com.android.support', module: 'support-annotations' |
|
27 |
+ }) |
|
28 |
+ compile 'com.android.support:appcompat-v7:26.+' |
|
29 |
+ compile 'com.android.support.constraint:constraint-layout:1.0.2' |
|
30 |
+ compile 'org.ocpsoft.prettytime:prettytime:3.2.7.Final' |
|
31 |
+ compile 'com.android.support:support-v4:26.+' |
|
32 |
+ compile 'com.android.support:support-vector-drawable:26.+' |
|
33 |
+ compile 'com.android.support:design:26.+' |
|
34 |
+ testCompile 'junit:junit:4.12' |
|
35 |
+} |
... | ... |
@@ -0,0 +1,25 @@ |
1 |
+# Add project specific ProGuard rules here. |
|
2 |
+# By default, the flags in this file are appended to flags specified |
|
3 |
+# in /Users/miles/Library/Android/sdk/tools/proguard/proguard-android.txt |
|
4 |
+# You can edit the include path and order by changing the proguardFiles |
|
5 |
+# directive in build.gradle. |
|
6 |
+# |
|
7 |
+# For more details, see |
|
8 |
+# http://developer.android.com/guide/developing/tools/proguard.html |
|
9 |
+ |
|
10 |
+# Add any project specific keep options here: |
|
11 |
+ |
|
12 |
+# If your project uses WebView with JS, uncomment the following |
|
13 |
+# and specify the fully qualified class name to the JavaScript interface |
|
14 |
+# class: |
|
15 |
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview { |
|
16 |
+# public *; |
|
17 |
+#} |
|
18 |
+ |
|
19 |
+# Uncomment this to preserve the line number information for |
|
20 |
+# debugging stack traces. |
|
21 |
+#-keepattributes SourceFile,LineNumberTable |
|
22 |
+ |
|
23 |
+# If you keep the line number information, uncomment this to |
|
24 |
+# hide the original source file name. |
|
25 |
+#-renamesourcefileattribute SourceFile |
... | ... |
@@ -0,0 +1,26 @@ |
1 |
+package app.librenews.io.librenews; |
|
2 |
+ |
|
3 |
+import android.content.Context; |
|
4 |
+import android.support.test.InstrumentationRegistry; |
|
5 |
+import android.support.test.runner.AndroidJUnit4; |
|
6 |
+ |
|
7 |
+import org.junit.Test; |
|
8 |
+import org.junit.runner.RunWith; |
|
9 |
+ |
|
10 |
+import static org.junit.Assert.*; |
|
11 |
+ |
|
12 |
+/** |
|
13 |
+ * Instrumentation test, which will execute on an Android device. |
|
14 |
+ * |
|
15 |
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> |
|
16 |
+ */ |
|
17 |
+@RunWith(AndroidJUnit4.class) |
|
18 |
+public class ExampleInstrumentedTest { |
|
19 |
+ @Test |
|
20 |
+ public void useAppContext() throws Exception { |
|
21 |
+ // Context of the app under test. |
|
22 |
+ Context appContext = InstrumentationRegistry.getTargetContext(); |
|
23 |
+ |
|
24 |
+ assertEquals("app.librenews.io.librenews", appContext.getPackageName()); |
|
25 |
+ } |
|
26 |
+} |
... | ... |
@@ -0,0 +1,36 @@ |
1 |
+<?xml version="1.0" encoding="utf-8"?> |
|
2 |
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
|
3 |
+ package="app.librenews.io.librenews"> |
|
4 |
+ |
|
5 |
+ <uses-permission android:name="android.permission.INTERNET" /> |
|
6 |
+ |
|
7 |
+ <application |
|
8 |
+ android:allowBackup="true" |
|
9 |
+ android:icon="@mipmap/ic_launcher" |
|
10 |
+ android:label="@string/app_name" |
|
11 |
+ android:roundIcon="@mipmap/ic_launcher_round" |
|
12 |
+ android:supportsRtl="true" |
|
13 |
+ android:theme="@style/AppTheme"> |
|
14 |
+ <activity android:name=".views.FlashView"> |
|
15 |
+ <intent-filter> |
|
16 |
+ <action android:name="android.intent.action.MAIN" /> |
|
17 |
+ |
|
18 |
+ <category android:name="android.intent.category.LAUNCHER" /> |
|
19 |
+ </intent-filter> |
|
20 |
+ </activity> |
|
21 |
+ |
|
22 |
+ <receiver |
|
23 |
+ android:name=".controllers.SyncManager$RefreshBroadcastReceiver" |
|
24 |
+ android:exported="true"> |
|
25 |
+ <intent-filter> |
|
26 |
+ <action android:name="android.intent.action.BOOT_COMPLETED" /> |
|
27 |
+ </intent-filter> |
|
28 |
+ </receiver> |
|
29 |
+ |
|
30 |
+ <activity |
|
31 |
+ android:name=".views.SettingsActivity" |
|
32 |
+ android:label="@string/title_activity_settings" |
|
33 |
+ android:theme="@style/AppTheme.NoActionBar"></activity> |
|
34 |
+ </application> |
|
35 |
+ |
|
36 |
+</manifest> |
... | ... |
@@ -0,0 +1,33 @@ |
1 |
+package app.librenews.io.librenews.controllers; |
|
2 |
+ |
|
3 |
+import android.app.NotificationManager; |
|
4 |
+import android.content.Context; |
|
5 |
+import android.content.SharedPreferences; |
|
6 |
+import android.net.Uri; |
|
7 |
+import android.preference.PreferenceManager; |
|
8 |
+import android.support.v4.app.NotificationCompat; |
|
9 |
+ |
|
10 |
+import app.librenews.io.librenews.R; |
|
11 |
+ |
|
12 |
+/** |
|
13 |
+ * Created by miles on 7/15/17. |
|
14 |
+ */ |
|
15 |
+ |
|
16 |
+public class DebugManager { |
|
17 |
+ public static void sendDebugNotification(String message, Context context){ |
|
18 |
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); |
|
19 |
+ if(prefs.getBoolean("debug", false)){ |
|
20 |
+ NotificationCompat.Builder mBuilder = |
|
21 |
+ new NotificationCompat.Builder(context) |
|
22 |
+ .setSmallIcon(R.drawable.ic_debug) |
|
23 |
+ .setStyle(new NotificationCompat.BigTextStyle() |
|
24 |
+ .bigText(message)) |
|
25 |
+ .setContentText(message); |
|
26 |
+ NotificationManager mNotificationManager = |
|
27 |
+ (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); |
|
28 |
+ |
|
29 |
+ mNotificationManager.notify(message.hashCode(), mBuilder.build()); |
|
30 |
+ System.out.println("LibreNews Debug: " + message); |
|
31 |
+ } |
|
32 |
+ } |
|
33 |
+} |
... | ... |
@@ -0,0 +1,228 @@ |
1 |
+package app.librenews.io.librenews.controllers; |
|
2 |
+ |
|
3 |
+import android.app.Activity; |
|
4 |
+import android.app.AlarmManager; |
|
5 |
+import android.app.NotificationManager; |
|
6 |
+import android.app.PendingIntent; |
|
7 |
+import android.content.Context; |
|
8 |
+import android.content.Intent; |
|
9 |
+import android.content.SharedPreferences; |
|
10 |
+import android.net.Uri; |
|
11 |
+import android.os.SystemClock; |
|
12 |
+import android.preference.PreferenceManager; |
|
13 |
+import android.support.v4.app.NotificationCompat; |
|
14 |
+import android.support.v4.widget.SwipeRefreshLayout; |
|
15 |
+import android.widget.Toast; |
|
16 |
+ |
|
17 |
+import org.json.JSONArray; |
|
18 |
+import org.json.JSONException; |
|
19 |
+import org.json.JSONObject; |
|
20 |
+ |
|
21 |
+import java.io.BufferedReader; |
|
22 |
+import java.io.FileInputStream; |
|
23 |
+import java.io.FileNotFoundException; |
|
24 |
+import java.io.FileOutputStream; |
|
25 |
+import java.io.IOException; |
|
26 |
+import java.io.InputStreamReader; |
|
27 |
+import java.net.MalformedURLException; |
|
28 |
+import java.net.URL; |
|
29 |
+import java.text.ParseException; |
|
30 |
+import java.util.ArrayList; |
|
31 |
+import java.util.Arrays; |
|
32 |
+import java.util.Comparator; |
|
33 |
+import java.util.List; |
|
34 |
+ |
|
35 |
+import app.librenews.io.librenews.R; |
|
36 |
+import app.librenews.io.librenews.models.Flash; |
|
37 |
+ |
|
38 |
+/** |
|
39 |
+ * Created by miles on 7/14/17. |
|
40 |
+ */ |
|
41 |
+ |
|
42 |
+public class FlashManager { |
|
43 |
+ |
|
44 |
+ int flashesToStoreInDatabase = 100; |
|
45 |
+ final String flashFileLocation = "flashes.json"; |
|
46 |
+ SharedPreferences prefs; |
|
47 |
+ String serverUrl; |
|
48 |
+ String serverName; |
|
49 |
+ Context context; |
|
50 |
+ |
|
51 |
+ public FlashManager(Context context) { |
|
52 |
+ this.context = context; |
|
53 |
+ this.prefs = PreferenceManager.getDefaultSharedPreferences(context); |
|
54 |
+ this.serverUrl = prefs.getString("server_url", "https://librenews.io/api"); |
|
55 |
+ try { |
|
56 |
+ loadFlashesFromStorage(); |
|
57 |
+ } catch (FileNotFoundException exception) { |
|
58 |
+ try { |
|
59 |
+ refresh(); |
|
60 |
+ } catch (Exception exception2) { |
|
61 |
+ Toast.makeText(context.getApplicationContext(), context.getResources().getString(R.string.internal_storage_setup_fail), Toast.LENGTH_LONG); |
|
62 |
+ exception2.printStackTrace(); |
|
63 |
+ } |
|
64 |
+ } catch (Exception exception) { |
|
65 |
+ Toast.makeText(context.getApplicationContext(), context.getResources().getString(R.string.internal_storage_read_fail), Toast.LENGTH_LONG); |
|
66 |
+ exception.printStackTrace(); |
|
67 |
+ } |
|
68 |
+ |
|
69 |
+ // first things first: get everything syncing! |
|
70 |
+ SyncManager syncManager = new SyncManager(context, this); |
|
71 |
+ syncManager.startSyncService(); |
|
72 |
+ } |
|
73 |
+ |
|
74 |
+ public ArrayList<Flash> loadFlashesFromStorage() throws JSONException, IOException, ParseException { |
|
75 |
+ FileInputStream inputStream = context.openFileInput(flashFileLocation); |
|
76 |
+ BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); |
|
77 |
+ StringBuilder builder = new StringBuilder(); |
|
78 |
+ ArrayList<Flash> latestPushedFlashes = new ArrayList<>(); |
|
79 |
+ |
|
80 |
+ String line; |
|
81 |
+ while ((line = reader.readLine()) != null) { |
|
82 |
+ builder.append(line); |
|
83 |
+ builder.append('\n'); // you'd think Java would have a better way of handling this, but no! |
|
84 |
+ } |
|
85 |
+ reader.close(); |
|
86 |
+ inputStream.close(); |
|
87 |
+ |
|
88 |
+ // System.out.println(builder); |
|
89 |
+ |
|
90 |
+ if(builder.toString().trim().equals("")){ |
|
91 |
+ throw new FileNotFoundException("No flash storage file exists or is empty."); |
|
92 |
+ } |
|
93 |
+ |
|
94 |
+ JSONArray jsonArray = new JSONArray(builder.toString()); |
|
95 |
+ for (int i = 0; i < jsonArray.length(); i++) { |
|
96 |
+ latestPushedFlashes.add(Flash.deserialize((JSONObject) jsonArray.get(i))); |
|
97 |
+ } |
|
98 |
+ return latestPushedFlashes; |
|
99 |
+ } |
|
100 |
+ |
|
101 |
+ public void writeFlashesToStorage(List<Flash> flashes) throws JSONException, IOException { |
|
102 |
+ FileOutputStream outputStream = context.openFileOutput(flashFileLocation, Context.MODE_PRIVATE); |
|
103 |
+ String out = convertFlashesToOutputString(flashes); |
|
104 |
+ outputStream.write(out.getBytes()); |
|
105 |
+ outputStream.close(); |
|
106 |
+ } |
|
107 |
+ |
|
108 |
+ public void sortPushedFlashes(){ |
|
109 |
+ // todo |
|
110 |
+ } |
|
111 |
+ |
|
112 |
+ private String convertFlashesToOutputString(List<Flash> flashes) throws JSONException { |
|
113 |
+ Flash[] sorted = flashes.toArray(new Flash[0]); |
|
114 |
+ Arrays.sort(sorted, new Comparator<Flash>() { |
|
115 |
+ public int compare(Flash a, Flash b){ |
|
116 |
+ return a.getDate().compareTo(b.getDate()); |
|
117 |
+ } |
|
118 |
+ }); |
|
119 |
+ |
|
120 |
+ int min = 0; |
|
121 |
+ int max = sorted.length - 1; |
|
122 |
+ if(max < 0){ |
|
123 |
+ max = 0; |
|
124 |
+ } |
|
125 |
+ if (max > flashesToStoreInDatabase) { |
|
126 |
+ min = max - flashesToStoreInDatabase; |
|
127 |
+ } |
|
128 |
+ JSONArray output = new JSONArray(); |
|
129 |
+ for (int i = min; i <= max; i++) { |
|
130 |
+ output.put(sorted[i].serialize()); |
|
131 |
+ } |
|
132 |
+ return output.toString(4); |
|
133 |
+ } |
|
134 |
+ |
|
135 |
+ public ArrayList<Flash> getLatestPushedFlashes() { |
|
136 |
+ try { |
|
137 |
+ return loadFlashesFromStorage(); |
|
138 |
+ }catch(Exception e){ |
|
139 |
+ DebugManager.sendDebugNotification("Unable to load flashes from storage: " + e.getLocalizedMessage(), context); |
|
140 |
+ e.printStackTrace(); |
|
141 |
+ } |
|
142 |
+ return new ArrayList<>(); |
|
143 |
+ } |
|
144 |
+ |
|
145 |
+ public void clearPushedFlashes() throws JSONException, IOException{ |
|
146 |
+ writeFlashesToStorage(new ArrayList<Flash>()); |
|
147 |
+ } |
|
148 |
+ |
|
149 |
+ public String getServerName() { |
|
150 |
+ return serverName; |
|
151 |
+ } |
|
152 |
+ |
|
153 |
+ public void pushFlashNotification(Flash flash) throws JSONException, IOException { |
|
154 |
+ if (!prefs.getBoolean("notifications_enabled", true)) { |
|
155 |
+ return; |
|
156 |
+ } |
|
157 |
+ Intent notificationIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(flash.getLink())); |
|
158 |
+ PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0); |
|
159 |
+ NotificationCompat.Builder mBuilder = |
|
160 |
+ new NotificationCompat.Builder(context) |
|
161 |
+ .setSmallIcon(R.drawable.ic_alert) |
|
162 |
+ .setContentTitle(flash.getChannel() + " • " + flash.getSource()) |
|
163 |
+ .setStyle(new NotificationCompat.BigTextStyle() |
|
164 |
+ .bigText(flash.getText())) |
|
165 |
+ .setContentText(flash.getText()) |
|
166 |
+ .setSound(Uri.parse(prefs.getString("notification_sound", "DEFAULT"))) |
|
167 |
+ .setContentIntent(pendingIntent); |
|
168 |
+ |
|
169 |
+ NotificationManager mNotificationManager = |
|
170 |
+ (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); |
|
171 |
+ |
|
172 |
+ mNotificationManager.notify(flash.getIdAsInteger(), mBuilder.build()); |
|
173 |
+ } |
|
174 |
+ |
|
175 |
+ public void refresh() { |
|
176 |
+ String newServerUrl = prefs.getString("server_url", "https://librenews.io/api"); |
|
177 |
+ if (!newServerUrl.equals(serverUrl)) { |
|
178 |
+ // they changed their server preferences! |
|
179 |
+ try { |
|
180 |
+ clearPushedFlashes(); |
|
181 |
+ } catch (IOException exception) { |
|
182 |
+ Toast.makeText(context.getApplicationContext(), context.getResources().getString(R.string.internal_storage_setup_fail), Toast.LENGTH_LONG); |
|
183 |
+ exception.printStackTrace(); |
|
184 |
+ } catch (JSONException exception) { |
|
185 |
+ Toast.makeText(context.getApplicationContext(), context.getResources().getString(R.string.internal_storage_setup_fail), Toast.LENGTH_LONG); |
|
186 |
+ exception.printStackTrace(); |
|
187 |
+ } |
|
188 |
+ } |
|
189 |
+ try { |
|
190 |
+ FlashRetreiver retreiver = new FlashRetreiver(new URL(serverUrl)); |
|
191 |
+ retreiver.retrieveFlashes(new FlashRetreiver.FlashHandler() { |
|
192 |
+ @Override |
|
193 |
+ public void success(Flash[] flashes, String serverName) { |
|
194 |
+ for (Flash f : flashes){ |
|
195 |
+ boolean pushed = false; |
|
196 |
+ for (Flash p : getLatestPushedFlashes()) { |
|
197 |
+ if (p.getId().equals(f.getId())) { |
|
198 |
+ pushed = true; |
|
199 |
+ } |
|
200 |
+ } |
|
201 |
+ try { |
|
202 |
+ if (!pushed) { |
|
203 |
+ pushFlashNotification(f); |
|
204 |
+ ArrayList<Flash> q = getLatestPushedFlashes(); |
|
205 |
+ q.add(f); |
|
206 |
+ writeFlashesToStorage(q); // lots of IO, but it's OK |
|
207 |
+ } |
|
208 |
+ }catch(Exception exception){ |
|
209 |
+ exception.printStackTrace(); |
|
210 |
+ DebugManager.sendDebugNotification("Error occurred while trying push notifications: " + exception.getLocalizedMessage(), context); |
|
211 |
+ } |
|
212 |
+ } |
|
213 |
+ } |
|
214 |
+ |
|
215 |
+ @Override |
|
216 |
+ public void failure(Exception exception) { |
|
217 |
+ exception.printStackTrace(); |
|
218 |
+ DebugManager.sendDebugNotification("An error occurred while trying to receive flashes: " + exception.getLocalizedMessage(), context); |
|
219 |
+ } |
|
220 |
+ }, context); |
|
221 |
+ } catch (MalformedURLException exception) { |
|
222 |
+ Toast.makeText(context.getApplicationContext(), context.getResources().getString(R.string.invalid_server_url), Toast.LENGTH_LONG); |
|
223 |
+ exception.printStackTrace(); |
|
224 |
+ } catch (Exception exception) { |
|
225 |
+ exception.printStackTrace(); |
|
226 |
+ } |
|
227 |
+ } |
|
228 |
+} |
... | ... |
@@ -0,0 +1,111 @@ |
1 |
+package app.librenews.io.librenews.controllers; |
|
2 |
+ |
|
3 |
+import android.content.Context; |
|
4 |
+import android.os.AsyncTask; |
|
5 |
+import android.os.Debug; |
|
6 |
+ |
|
7 |
+import org.json.JSONArray; |
|
8 |
+import org.json.JSONException; |
|
9 |
+import org.json.JSONObject; |
|
10 |
+ |
|
11 |
+import java.io.BufferedReader; |
|
12 |
+import java.io.IOException; |
|
13 |
+import java.io.InputStreamReader; |
|
14 |
+import java.net.HttpURLConnection; |
|
15 |
+import java.net.URL; |
|
16 |
+import java.text.ParseException; |
|
17 |
+import java.util.ArrayList; |
|
18 |
+ |
|
19 |
+import javax.net.ssl.HttpsURLConnection; |
|
20 |
+ |
|
21 |
+import app.librenews.io.librenews.models.Flash; |
|
22 |
+ |
|
23 |
+import static app.librenews.io.librenews.controllers.DebugManager.sendDebugNotification; |
|
24 |
+ |
|
25 |
+public class FlashRetreiver { |
|
26 |
+ interface FlashHandler { |
|
27 |
+ void success(Flash[] flashes, String serverName); |
|
28 |
+ |
|
29 |
+ void failure(Exception exception); |
|
30 |
+ } |
|
31 |
+ |
|
32 |
+ URL serverLocation; |
|
33 |
+ |
|
34 |
+ public FlashRetreiver(URL serverLocation) { |
|
35 |
+ this.serverLocation = serverLocation; |
|
36 |
+ } |
|
37 |
+ |
|
38 |
+ |
|
39 |
+ private Flash[] convertJsonToFlashes(JSONArray jsonFlashes) throws JSONException, ParseException { |
|
40 |
+ ArrayList<Flash> flashes = new ArrayList<Flash>(); |
|
41 |
+ for (int i = 0; i < jsonFlashes.length(); i++) { |
|
42 |
+ JSONObject jsonFlash = (JSONObject) jsonFlashes.get(i); |
|
43 |
+ Flash flash = Flash.deserialize(jsonFlash); |
|
44 |
+ flashes.add(flash); |
|
45 |
+ } |
|
46 |
+ return flashes.toArray(new Flash[0]); |
|
47 |
+ } |
|
48 |
+ |
|
49 |
+ Flash[] flashes = null; |
|
50 |
+ String serverName = null; |
|
51 |
+ |
|
52 |
+ private boolean retreiveFlashesNonAsync(Context context) throws IOException, JSONException, ParseException{ |
|
53 |
+ if (!serverLocation.getProtocol().equals("https")) { |
|
54 |
+ throw new SecurityException("Flashes may only be retrieved over a secure HTTPS connection!"); |
|
55 |
+ } |
|
56 |
+ sendDebugNotification("Retrieving flashes from " + serverLocation, context); |
|
57 |
+ HttpsURLConnection urlConnection = (HttpsURLConnection) serverLocation.openConnection(); |
|
58 |
+ urlConnection.setRequestProperty("Content-Type", "application/json"); |
|
59 |
+ urlConnection.setRequestMethod("GET"); |
|
60 |
+ urlConnection.setReadTimeout(150000); |
|
61 |
+ urlConnection.setConnectTimeout(15000); |
|
62 |
+ urlConnection.connect(); |
|
63 |
+ if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) { |
|
64 |
+ BufferedReader reader = new BufferedReader(new InputStreamReader( |
|
65 |
+ urlConnection.getInputStream()), 8); |
|
66 |
+ StringBuilder sb = new StringBuilder(); |
|
67 |
+ String line = null; |
|
68 |
+ while ((line = reader.readLine()) != null) { |
|
69 |
+ sb.append(line + "\n"); |
|
70 |
+ } |
|
71 |
+ String data = sb.toString(); |
|
72 |
+ JSONObject jsonData = new JSONObject(data); |
|
73 |
+ serverName = jsonData.getString("server"); |
|
74 |
+ flashes = convertJsonToFlashes(jsonData.getJSONArray("latest")); |
|
75 |
+ } |
|
76 |
+ sendDebugNotification("Flashes found: " + flashes.length, context); |
|
77 |
+ return true; |
|
78 |
+ } |
|
79 |
+ |
|
80 |
+ public void retrieveFlashes(final FlashHandler handler, final Context context) { |
|
81 |
+ final URL serverLocation = this.serverLocation; |
|
82 |
+ class JSONAsyncTask extends AsyncTask<String, Void, Boolean> { |
|
83 |
+ @Override |
|
84 |
+ protected void onPreExecute() { |
|
85 |
+ super.onPreExecute(); |
|
86 |
+ } |
|
87 |
+ |
|
88 |
+ @Override |
|
89 |
+ protected Boolean doInBackground(String... params) { |
|
90 |
+ try { |
|
91 |
+ return retreiveFlashesNonAsync(context); |
|
92 |
+ }catch(Exception e){ |
|
93 |
+ handler.failure(e); |
|
94 |
+ e.printStackTrace(); |
|
95 |
+ } |
|
96 |
+ return false; |
|
97 |
+ } |
|
98 |
+ |
|
99 |
+ protected void onPostExecute(Boolean result) { |
|
100 |
+ if (!result || flashes == null) { |
|
101 |
+ handler.failure(new Exception("An error occurred while trying to receive flashes!")); |
|
102 |
+ DebugManager.sendDebugNotification("An error occurred while trying to receive flashes!", context); |
|
103 |
+ return; |
|
104 |
+ } |
|
105 |
+ handler.success(flashes, serverName); |
|
106 |
+ } |
|
107 |
+ } |
|
108 |
+ DebugManager.sendDebugNotification("Performing an asynchronous flash retrieval...", context); |
|
109 |
+ new JSONAsyncTask().execute(); |
|
110 |
+ } |
|
111 |
+} |
... | ... |
@@ -0,0 +1,70 @@ |
1 |
+package app.librenews.io.librenews.controllers; |
|
2 |
+ |
|
3 |
+import android.app.Activity; |
|
4 |
+import android.app.AlarmManager; |
|
5 |
+import android.app.NotificationManager; |
|
6 |
+import android.app.PendingIntent; |
|
7 |
+import android.content.BroadcastReceiver; |
|
8 |
+import android.content.Context; |
|
9 |
+import android.content.Intent; |
|
10 |
+import android.content.SharedPreferences; |
|
11 |
+import android.os.SystemClock; |
|
12 |
+import android.preference.PreferenceManager; |
|
13 |
+import android.support.v4.app.NotificationCompat; |
|
14 |
+ |
|
15 |
+import app.librenews.io.librenews.R; |
|
16 |
+ |
|
17 |
+import static app.librenews.io.librenews.controllers.DebugManager.sendDebugNotification; |
|
18 |
+ |
|
19 |
+/** |
|
20 |
+ * Created by miles on 7/15/17. |
|
21 |
+ */ |
|
22 |
+ |
|
23 |
+public class SyncManager { |
|
24 |
+ Context context; |
|
25 |
+ FlashManager flashManager; |
|
26 |
+ SharedPreferences prefs; |
|
27 |
+ |
|
28 |
+ public SyncManager(Context context, FlashManager flashManager) { |
|
29 |
+ this.context = context; |
|
30 |
+ this.flashManager = flashManager; |
|
31 |
+ this.prefs = PreferenceManager.getDefaultSharedPreferences(context); |
|
32 |
+ } |
|
33 |
+ |
|
34 |
+ public void startSyncService(){ |
|
35 |
+ Intent intent = new Intent(context, RefreshBroadcastReceiver.class); |
|
36 |
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 11719, intent, 0); |
|
37 |
+ String val = prefs.getString("refresh_preference", "60"); |
|
38 |
+ int syncRaw = Integer.valueOf(val); |
|
39 |
+ long syncInterval = syncRaw*60000; // milliseconds |
|
40 |
+ AlarmManager alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); |
|
41 |
+ alarmMgr.cancel(pendingIntent); // to ensure we don't have two sync daemons running |
|
42 |
+ if(!prefs.getBoolean("automatically_refresh", true)){ |
|
43 |
+ sendDebugNotification("Sync service not enabled (would have been @ " + syncInterval + ")", context); |
|
44 |
+ return; |
|
45 |
+ // don't sync if they have disabled syncs! |
|
46 |
+ } |
|
47 |
+ alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, |
|
48 |
+ SystemClock.elapsedRealtime() + syncInterval, |
|
49 |
+ syncInterval, pendingIntent); |
|
50 |
+ sendDebugNotification("Sync service started at an interval of " + syncInterval, context); |
|
51 |
+ } |
|
52 |
+ |
|
53 |
+ public static class RefreshBroadcastReceiver extends BroadcastReceiver { |
|
54 |
+ @Override |
|
55 |
+ public void onReceive(Context context, Intent intent) { |
|
56 |
+ sendDebugNotification("Syncing with server...", context); |
|
57 |
+ FlashManager flashManager = new FlashManager(context); |
|
58 |
+ flashManager.refresh(); |
|
59 |
+ sendDebugNotification("Sync completed!", context); |
|
60 |
+ } |
|
61 |
+ } |
|
62 |
+ |
|
63 |
+ public static class BootReceiver extends BroadcastReceiver { |
|
64 |
+ @Override |
|
65 |
+ public void onReceive(Context context, Intent intent){ |
|
66 |
+ SyncManager manager = new SyncManager(context, new FlashManager(context)); |
|
67 |
+ manager.startSyncService(); |
|
68 |
+ } |
|
69 |
+ } |
|
70 |
+} |
... | ... |
@@ -0,0 +1,119 @@ |
1 |
+package app.librenews.io.librenews.models; |
|
2 |
+ |
|
3 |
+import java.text.ParseException; |
|
4 |
+import java.text.SimpleDateFormat; |
|
5 |
+import java.util.Date; |
|
6 |
+import java.util.Locale; |
|
7 |
+ |
|
8 |
+import org.json.JSONException; |
|
9 |
+import org.json.JSONObject; |
|
10 |
+import org.ocpsoft.prettytime.PrettyTime; |
|
11 |
+ |
|
12 |
+public class Flash { |
|
13 |
+ String channel; |
|
14 |
+ String id; |
|
15 |
+ String link; |
|
16 |
+ String source; |
|
17 |
+ String text; |
|
18 |
+ Date date; |
|
19 |
+ |
|
20 |
+ public Flash(String channel, String id, String link, String source, String text, Date date) { |
|
21 |
+ this.channel = channel; |
|
22 |
+ this.id = id; |
|
23 |
+ this.link = link; |
|
24 |
+ this.source = source; |
|
25 |
+ this.text = text; |
|
26 |
+ this.date = date; |
|
27 |
+ } |
|
28 |
+ |
|
29 |
+ public String getChannel() { |
|
30 |
+ return channel; |
|
31 |
+ } |
|
32 |
+ |
|
33 |
+ public void setChannel(String channel) { |
|
34 |
+ this.channel = channel; |
|
35 |
+ } |
|
36 |
+ |
|
37 |
+ public int getIdAsInteger() { |
|
38 |
+ try{ |
|
39 |
+ return Integer.parseInt(id); |
|
40 |
+ }catch(NumberFormatException e){ |
|
41 |
+ return id.hashCode(); |
|
42 |
+ } |
|
43 |
+ } |
|
44 |
+ |
|
45 |
+ public String getId(){ |
|
46 |
+ return id; |
|
47 |
+ } |
|
48 |
+ |
|
49 |
+ public void setId(String id) { |
|
50 |
+ this.id = id; |
|
51 |
+ } |
|
52 |
+ |
|
53 |
+ public String getLink() { |
|
54 |
+ return link; |
|
55 |
+ } |
|
56 |
+ |
|
57 |
+ public void setLink(String link) { |
|
58 |
+ this.link = link; |
|
59 |
+ } |
|
60 |
+ |
|
61 |
+ public String getSource() { |
|
62 |
+ return source; |
|
63 |
+ } |
|
64 |
+ |
|
65 |
+ public void setSource(String source) { |
|
66 |
+ this.source = source; |
|
67 |
+ } |
|
68 |
+ |
|
69 |
+ public String getText() { |
|
70 |
+ return text; |
|
71 |
+ } |
|
72 |
+ |
|
73 |
+ public void setText(String text) { |
|
74 |
+ this.text = text; |
|
75 |
+ } |
|
76 |
+ |
|
77 |
+ public Date getDate() { |
|
78 |
+ return date; |
|
79 |
+ } |
|
80 |
+ |
|
81 |
+ public void setDate(Date date) { |
|
82 |
+ this.date = date; |
|
83 |
+ } |
|
84 |
+ |
|
85 |
+ public String getHumanReadableRelativeTime() { |
|
86 |
+ PrettyTime p = new PrettyTime(); |
|
87 |
+ return p.format(getDate()); |
|
88 |
+ } |
|
89 |
+ |
|
90 |
+ public JSONObject serialize() throws JSONException { |
|
91 |
+ JSONObject object = new JSONObject(); |
|
92 |
+ object.put("channel", channel); |
|
93 |
+ object.put("id", id); |
|
94 |
+ object.put("link", link); |
|
95 |
+ object.put("text", text); |
|
96 |
+ object.put("time", date); |
|
97 |
+ object.put("source", source); |
|
98 |
+ return object; |
|
99 |
+ } |
|
100 |
+ |
|
101 |
+ public static Date getTwitterDate(String date) throws ParseException { |
|
102 |
+ final String twitter_format = "EEE MMM dd HH:mm:ss Z yyyy"; |
|
103 |
+ SimpleDateFormat sf = new SimpleDateFormat(twitter_format, Locale.ENGLISH); |
|
104 |
+ sf.setLenient(true); |
|
105 |
+ return sf.parse(date); |
|
106 |
+ } |
|
107 |
+ |
|
108 |
+ public static Flash deserialize(JSONObject jsonFlash) throws JSONException, ParseException { |
|
109 |
+ Flash flash = new Flash( |
|
110 |
+ (String) jsonFlash.get("channel"), |
|
111 |
+ (String) jsonFlash.get("id"), |
|
112 |
+ (String) jsonFlash.get("link"), |
|
113 |
+ (String) jsonFlash.get("source"), |
|
114 |
+ (String) jsonFlash.get("text"), |
|
115 |
+ getTwitterDate((String) jsonFlash.get("time")) |
|
116 |
+ ); |
|
117 |
+ return flash; |
|
118 |
+ } |
|
119 |
+} |
... | ... |
@@ -0,0 +1,41 @@ |
1 |
+package app.librenews.io.librenews.views; |
|
2 |
+ |
|
3 |
+import android.content.Intent; |
|
4 |
+import android.preference.PreferenceActivity; |
|
5 |
+import android.preference.PreferenceManager; |
|
6 |
+import android.support.v4.widget.SwipeRefreshLayout; |
|
7 |
+import android.support.v7.app.AppCompatActivity; |
|
8 |
+import android.os.Bundle; |
|
9 |
+import android.view.View; |
|
10 |
+ |
|
11 |
+import app.librenews.io.librenews.R; |
|
12 |
+import app.librenews.io.librenews.controllers.FlashManager; |
|
13 |
+import app.librenews.io.librenews.controllers.SyncManager; |
|
14 |
+ |
|
15 |
+public class FlashView extends AppCompatActivity { |
|
16 |
+ FlashManager manager; |
|
17 |
+ |
|
18 |
+ @Override |
|
19 |
+ protected void onCreate(Bundle savedInstanceState) { |
|
20 |
+ super.onCreate(savedInstanceState); |
|
21 |
+ setContentView(R.layout.activity_flash_view); |
|
22 |
+ manager = new FlashManager(this); |
|
23 |
+ findViewById(R.id.settings_button).setOnClickListener( |
|
24 |
+ new View.OnClickListener() { |
|
25 |
+ @Override |
|
26 |
+ public void onClick(View view) { |
|
27 |
+ Intent intent = new Intent(FlashView.this, SettingsActivity.class); |
|
28 |
+ startActivity(intent); |
|
29 |
+ } |
|
30 |
+ } |
|
31 |
+ ); |
|
32 |
+ final SwipeRefreshLayout srl = (SwipeRefreshLayout) findViewById(R.id.swiperefresh); |
|
33 |
+ srl.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { |
|
34 |
+ @Override |
|
35 |
+ public void onRefresh() { |
|
36 |
+ new FlashManager(getApplicationContext()).refresh(); |
|
37 |
+ srl.setRefreshing(false); |
|
38 |
+ } |
|
39 |
+ }); |
|
40 |
+ } |
|
41 |
+} |
... | ... |
@@ -0,0 +1,37 @@ |
1 |
+package app.librenews.io.librenews.views; |
|
2 |
+ |
|
3 |
+import android.content.SharedPreferences; |
|
4 |
+import android.os.Bundle; |
|
5 |
+import android.preference.PreferenceActivity; |
|
6 |
+import android.preference.PreferenceManager; |
|
7 |
+import android.support.design.widget.FloatingActionButton; |
|
8 |
+import android.support.design.widget.Snackbar; |
|
9 |
+import android.support.v7.app.AppCompatActivity; |
|
10 |
+import android.support.v7.widget.Toolbar; |
|
11 |
+import android.view.View; |
|
12 |
+ |
|
13 |
+import app.librenews.io.librenews.R; |
|
14 |
+import app.librenews.io.librenews.controllers.FlashManager; |
|
15 |
+import app.librenews.io.librenews.controllers.SyncManager; |
|
16 |
+ |
|
17 |
+public class SettingsActivity extends PreferenceActivity { |
|
18 |
+ |
|
19 |
+ @Override |
|
20 |
+ protected void onCreate(Bundle savedInstanceState) { |
|
21 |
+ super.onCreate(savedInstanceState); |
|
22 |
+ addPreferencesFromResource(R.xml.preferences); |
|
23 |
+ SharedPreferences.OnSharedPreferenceChangeListener spChanged = new |
|
24 |
+ SharedPreferences.OnSharedPreferenceChangeListener() { |
|
25 |
+ @Override |
|
26 |
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, |
|
27 |
+ String key) { |
|
28 |
+ if(key.equals("refresh_preference") || key.equals("automatically_refresh")){ |
|
29 |
+ new SyncManager(getApplicationContext(), new FlashManager(getApplicationContext())).startSyncService(); |
|
30 |
+ } |
|
31 |
+ } |
|
32 |
+ }; |
|
33 |
+ |
|
34 |
+ PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).registerOnSharedPreferenceChangeListener(spChanged); |
|
35 |
+ } |
|
36 |
+ |
|
37 |
+} |
... | ... |
@@ -0,0 +1,16 @@ |
1 |
+package app.librenews.io.librenews.views; |
|
2 |
+ |
|
3 |
+import android.preference.PreferenceFragment; |
|
4 |
+import android.os.Bundle; |
|
5 |
+ |
|
6 |
+import app.librenews.io.librenews.R; |
|
7 |
+ |
|
8 |
+public class SettingsActivityFragment extends PreferenceFragment { |
|
9 |
+ @Override |
|
10 |
+ public void onCreate(final Bundle savedInstanceState) { |
|
11 |
+ super.onCreate(savedInstanceState); |
|
12 |
+ |
|
13 |
+ // Load the preferences from an XML resource |
|
14 |
+ addPreferencesFromResource(R.xml.preferences); |
|
15 |
+ } |
|
16 |
+} |
... | ... |
@@ -0,0 +1,9 @@ |
1 |
+<vector xmlns:android="http://schemas.android.com/apk/res/android" |
|
2 |
+ android:width="24dp" |
|
3 |
+ android:height="24dp" |
|
4 |
+ android:viewportWidth="24.0" |
|
5 |
+ android:viewportHeight="24.0"> |
|
6 |
+ <path |
|
7 |
+ android:fillColor="#FF000000" |
|
8 |
+ android:pathData="M23,12l-2.44,-2.78 0.34,-3.68 -3.61,-0.82 -1.89,-3.18L12,3 8.6,1.54 6.71,4.72l-3.61,0.81 0.34,3.68L1,12l2.44,2.78 -0.34,3.69 3.61,0.82 1.89,3.18L12,21l3.4,1.46 1.89,-3.18 3.61,-0.82 -0.34,-3.68L23,12zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z"/> |
|
9 |
+</vector> |
... | ... |
@@ -0,0 +1,9 @@ |
1 |
+<vector xmlns:android="http://schemas.android.com/apk/res/android" |
|
2 |
+ android:width="24dp" |
|
3 |
+ android:height="24dp" |
|
4 |
+ android:viewportWidth="24.0" |
|
5 |
+ android:viewportHeight="24.0"> |
|
6 |
+ <path |
|
7 |
+ android:fillColor="#FF000000" |
|
8 |
+ android:pathData="M9.4,16.6L4.8,12l4.6,-4.6L8,6l-6,6 6,6 1.4,-1.4zM14.6,16.6l4.6,-4.6 -4.6,-4.6L16,6l6,6 -6,6 -1.4,-1.4z"/> |
|
9 |
+</vector> |
... | ... |
@@ -0,0 +1,9 @@ |
1 |
+<vector xmlns:android="http://schemas.android.com/apk/res/android" |
|
2 |
+ android:width="24dp" |
|
3 |
+ android:height="24dp" |
|
4 |
+ android:viewportHeight="24.0" |
|
5 |
+ android:viewportWidth="24.0"> |
|
6 |
+ <path |
|
7 |
+ android:fillColor="#FF000000" |
|
8 |
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm1,15h-2v-6h2v6zm0,-8h-2V7h2v2z" /> |
|
9 |
+</vector> |
... | ... |
@@ -0,0 +1,9 @@ |
1 |
+<vector xmlns:android="http://schemas.android.com/apk/res/android" |
|
2 |
+ android:width="24dp" |
|
3 |
+ android:height="24dp" |
|
4 |
+ android:viewportHeight="24.0" |
|
5 |
+ android:viewportWidth="24.0"> |
|
6 |
+ <path |
|
7 |
+ android:fillColor="#FF000000" |
|
8 |
+ android:pathData="M11.5,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zm6.5,-6v-5.5c0,-3.07 -2.13,-5.64 -5,-6.32V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5v0.68c-2.87,0.68 -5,3.25 -5,6.32V16l-2,2v1h17v-1l-2,-2z" /> |
|
9 |
+</vector> |
... | ... |
@@ -0,0 +1,9 @@ |
1 |
+<vector xmlns:android="http://schemas.android.com/apk/res/android" |
|
2 |
+ android:width="24dp" |
|
3 |
+ android:height="24dp" |
|
4 |
+ android:viewportHeight="24.0" |
|
5 |
+ android:viewportWidth="24.0"> |
|
6 |
+ <path |
|
7 |
+ android:fillColor="#FF000000" |
|
8 |
+ android:pathData="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01,-.25 1.97,-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0,-4.42,-3.58,-8,-8,-8zm0 14c-3.31 0,-6,-2.69,-6,-6 0,-1.01.25,-1.97.7,-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4,-4,-4,-4v3z" /> |
|
9 |
+</vector> |
... | ... |
@@ -0,0 +1,88 @@ |
1 |
+<?xml version="1.0" encoding="utf-8"?> |
|
2 |
+<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|
3 |
+ xmlns:tools="http://schemas.android.com/tools" |
|
4 |
+ xmlns:app="http://schemas.android.com/apk/res-auto" |
|
5 |
+ android:layout_width="match_parent" |
|
6 |
+ android:layout_height="match_parent"> |
|
7 |
+ |
|
8 |
+ <android.support.constraint.ConstraintLayout |
|
9 |
+ android:layout_width="match_parent" |
|
10 |
+ android:layout_height="match_parent"> |
|
11 |
+ |
|
12 |
+ <android.support.v7.widget.Toolbar |
|
13 |
+ android:id="@+id/toolbar" |
|
14 |
+ android:layout_width="0dp" |
|
15 |
+ android:layout_height="wrap_content" |
|
16 |
+ android:background="@color/colorPrimarySub" |
|
17 |
+ android:minHeight="?attr/actionBarSize" |
|
18 |
+ android:theme="?attr/actionBarTheme" |
|
19 |
+ app:layout_constraintLeft_toLeftOf="parent" |
|
20 |
+ app:layout_constraintRight_toRightOf="parent" |
|
21 |
+ app:layout_constraintTop_toTopOf="parent" /> |
|
22 |
+ |
|
23 |
+ <Button |
|
24 |
+ android:id="@+id/settings_button" |
|
25 |
+ style="@style/Widget.AppCompat.Button.Borderless.Colored" |
|
26 |
+ android:layout_width="wrap_content" |
|
27 |
+ android:layout_height="wrap_content" |
|
28 |
+ android:layout_marginBottom="8dp" |
|
29 |
+ android:layout_marginEnd="8dp" |
|
30 |
+ android:layout_marginRight="8dp" |
|
31 |
+ android:layout_marginTop="8dp" |
|
32 |
+ android:text="@string/action_settings" |
|
33 |
+ android:textColor="@android:color/background_light" |
|
34 |
+ app:layout_constraintBottom_toBottomOf="@+id/toolbar" |
|
35 |
+ app:layout_constraintRight_toRightOf="@+id/toolbar" |
|
36 |
+ app:layout_constraintTop_toTopOf="@+id/toolbar" /> |
|
37 |
+ |
|
38 |
+ <TextView |
|
39 |
+ android:id="@+id/server_name" |
|
40 |
+ android:layout_width="0dp" |
|
41 |
+ android:layout_height="wrap_content" |
|
42 |
+ android:layout_marginBottom="8dp" |
|
43 |
+ android:layout_marginLeft="16dp" |
|
44 |
+ android:layout_marginStart="16dp" |
|
45 |
+ android:layout_marginTop="8dp" |
|
46 |
+ android:text="[server name]" |
|
47 |
+ android:textAppearance="@style/TextAppearance.AppCompat.Medium.Inverse" |
|
48 |
+ app:layout_constraintBottom_toBottomOf="@+id/toolbar" |
|
49 |
+ app:layout_constraintLeft_toLeftOf="@+id/toolbar" |
|
50 |
+ app:layout_constraintTop_toTopOf="@+id/toolbar" |
|
51 |
+ app:layout_constraintVertical_bias="0.444" /> |
|
52 |
+ |
|
53 |
+ <android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|
54 |
+ android:id="@+id/swiperefresh" |
|
55 |
+ android:layout_width="360dp" |
|
56 |
+ android:layout_height="0dp" |
|
57 |
+ app:layout_constraintTop_toBottomOf="@+id/toolbar" |
|
58 |
+ android:layout_marginLeft="8dp" |
|
59 |
+ app:layout_constraintLeft_toLeftOf="parent" |
|
60 |
+ app:layout_constraintBottom_toBottomOf="parent" |
|
61 |
+ android:layout_marginStart="8dp" |
|
62 |
+ app:layout_constraintVertical_bias="0.5"> |
|
63 |
+ |
|
64 |
+ <ScrollView |
|
65 |
+ android:layout_width="match_parent" |
|
66 |
+ android:layout_height="8dp" |
|
67 |
+ tools:layout_editor_absoluteX="8dp" |
|
68 |
+ tools:layout_editor_absoluteY="137dp"> |
|
69 |
+ |
|
70 |
+ <LinearLayout |
|
71 |
+ android:layout_width="match_parent" |
|
72 |
+ android:layout_height="wrap_content" |
|
73 |
+ android:orientation="vertical"> |
|
74 |
+ |
|
75 |
+ <android.support.constraint.ConstraintLayout |
|
76 |
+ android:layout_width="match_parent" |
|
77 |
+ android:layout_height="match_parent"> |
|
78 |
+ |
|
79 |
+ </android.support.constraint.ConstraintLayout> |
|
80 |
+ </LinearLayout> |
|
81 |
+ </ScrollView> |
|
82 |
+ |
|
83 |
+ </android.support.v4.widget.SwipeRefreshLayout> |
|
84 |
+ |
|
85 |
+ |
|
86 |
+ </android.support.constraint.ConstraintLayout> |
|
87 |
+ |
|
88 |
+</android.support.design.widget.CoordinatorLayout> |
... | ... |
@@ -0,0 +1,11 @@ |
1 |
+<?xml version="1.0" encoding="utf-8"?> |
|
2 |
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|
3 |
+ xmlns:app="http://schemas.android.com/apk/res-auto" |
|
4 |
+ xmlns:tools="http://schemas.android.com/tools" |
|
5 |
+ android:layout_width="match_parent" |
|
6 |
+ android:layout_height="match_parent" |
|
7 |
+ tools:context="app.librenews.io.librenews.views.SettingsActivityFragment" |
|
8 |
+ tools:showIn="@layout/activity_settings"> |
|
9 |
+ |
|
10 |
+ |
|
11 |
+</android.support.constraint.ConstraintLayout> |
... | ... |
@@ -0,0 +1,10 @@ |
1 |
+<menu xmlns:android="http://schemas.android.com/apk/res/android" |
|
2 |
+ xmlns:app="http://schemas.android.com/apk/res-auto" |
|
3 |
+ xmlns:tools="http://schemas.android.com/tools" |
|
4 |
+ tools:context="app.librenews.io.librenews.views.SettingsActivity"> |
|
5 |
+ <item |
|
6 |
+ android:id="@+id/action_settings" |
|
7 |
+ android:orderInCategory="100" |
|
8 |
+ android:title="@string/action_settings" |
|
9 |
+ app:showAsAction="never" /> |
|
10 |
+</menu> |
... | ... |
@@ -0,0 +1,31 @@ |
1 |
+<?xml version="1.0" encoding="utf-8"?> |
|
2 |
+<resources> |
|
3 |
+ <string-array name="refresh_rates"> |
|
4 |
+ <item>1 minute</item> |
|
5 |
+ <item>2 minutes</item> |
|
6 |
+ <item>5 minutes</item> |
|
7 |
+ <item>10 minutes</item> |
|
8 |
+ <item>15 minutes</item> |
|
9 |
+ <item>30 minutes</item> |
|
10 |
+ <item>1 hour</item> |
|
11 |
+ <item>2 hours</item> |
|
12 |
+ <item>3 hours</item> |
|
13 |
+ <item>6 hours</item> |
|
14 |
+ <item>12 hours</item> |
|
15 |
+ <item>24 hours</item> |
|
16 |
+ </string-array> |
|
17 |
+ <string-array name="refresh_values"> |
|
18 |
+ <item>1</item> |
|
19 |
+ <item>2</item> |
|
20 |
+ <item>5</item> |
|
21 |
+ <item>10</item> |
|
22 |
+ <item>15</item> |
|
23 |
+ <item>30</item> |
|
24 |
+ <item>60</item> |
|
25 |
+ <item>120</item> |
|
26 |
+ <item>180</item> |
|
27 |
+ <item>360</item> |
|
28 |
+ <item>720</item> |
|
29 |
+ <item>1440</item> |
|
30 |
+ </string-array> |
|
31 |
+</resources> |
... | ... |
@@ -0,0 +1,8 @@ |
1 |
+<resources> |
|
2 |
+ <declare-styleable name="FlashEmbedView"> |
|
3 |
+ <attr name="exampleString" format="string" /> |
|
4 |
+ <attr name="exampleDimension" format="dimension" /> |
|
5 |
+ <attr name="exampleColor" format="color" /> |
|
6 |
+ <attr name="exampleDrawable" format="color|reference" /> |
|
7 |
+ </declare-styleable> |
|
8 |
+</resources> |
... | ... |
@@ -0,0 +1,7 @@ |
1 |
+<?xml version="1.0" encoding="utf-8"?> |
|
2 |
+<resources> |
|
3 |
+ <color name="colorPrimary">#3F51B5</color> |
|
4 |
+ <color name="colorPrimaryDark">#303F9F</color> |
|
5 |
+ <color name="colorAccent">#FF4081</color> |
|
6 |
+ <color name="colorPrimarySub">#5b64c0</color> |
|
7 |
+</resources> |
... | ... |
@@ -0,0 +1,3 @@ |
1 |
+<resources> |
|
2 |
+ <dimen name="fab_margin">16dp</dimen> |
|
3 |
+</resources> |
... | ... |
@@ -0,0 +1,96 @@ |
1 |
+<resources> |
|
2 |
+ <string name="app_name">LibreNews</string> |
|
3 |
+ <string name="server_settings_label">Server Settings</string> |
|
4 |
+ <string name="default_server_url">https://librenews.io/api</string> |
|
5 |
+ <string name="server_url_label">Server</string> |
|
6 |
+ <string name="refresh_rate_label">Refresh rate</string> |
|
7 |
+ <string name="notification_preferences_label">Notification Settings</string> |
|
8 |
+ <string name="notifications_enabled_label">Notifications enabled</string> |
|
9 |
+ <string name="auto_refresh_label">Automatically refresh</string> |
|
10 |
+ <string name="server_url_description">The URL of the server to get newsflashes from. Must be HTTPS.</string> |
|
11 |
+ <string name="auto_refresh_description">Whether LibreNews should automatically refresh in the background.</string> |
|
12 |
+ <string name="refresh_rate_description">How frequently LibreNews should contact the newsflash server in the background to check for new notifications.</string> |
|
13 |
+ <string name="notification_sound_label">Notification sound</string> |
|
14 |
+ <string name="notification_sound_description">The sound that LibreNews will make when a notification is received.</string> |
|
15 |
+ <string name="notifications_enabled_description">Whether LibreNews shoud send you notifications.</string> |
|
16 |
+ <string name="internal_storage_setup_fail">Unable to set up internal storage!</string> |
|
17 |
+ <string name="internal_storage_read_fail">Unable to read from internal storage!</string> |
|
18 |
+ <string name="invalid_server_url">Invalid server URL!</string> |
|
19 |
+ <string name="debug_label">Debug</string> |
|
20 |
+ <string name="debug_mode_label">Debug mode</string> |
|
21 |
+ <string name="debug_mode_description">Whether LibreNews should send you lots of debug messages (via notifications)</string> |
|
22 |
+ <string name="title_activity_settings">Settings</string> |
|
23 |
+ |
|
24 |
+ <!-- Strings related to Settings --> |
|
25 |
+ |
|
26 |
+ <!-- Example General settings --> |
|
27 |
+ <string name="pref_header_general">General</string> |
|
28 |
+ |
|
29 |
+ <string name="pref_title_social_recommendations">Enable social recommendations</string> |
|
30 |
+ <string name="pref_description_social_recommendations">Recommendations for people to contact |
|
31 |
+ based on your message history |
|
32 |
+ </string> |
|
33 |
+ |
|
34 |
+ <string name="pref_title_display_name">Display name</string> |
|
35 |
+ <string name="pref_default_display_name">John Smith</string> |
|
36 |
+ |
|
37 |
+ <string name="pref_title_add_friends_to_messages">Add friends to messages</string> |
|
38 |
+ <string-array name="pref_example_list_titles"> |
|
39 |
+ <item>Always</item> |
|
40 |
+ <item>When possible</item> |
|
41 |
+ <item>Never</item> |
|
42 |
+ </string-array> |
|
43 |
+ <string-array name="pref_example_list_values"> |
|
44 |
+ <item>1</item> |
|
45 |
+ <item>0</item> |
|
46 |
+ <item>-1</item> |
|
47 |
+ </string-array> |
|
48 |
+ |
|
49 |
+ <!-- Example settings for Data & Sync --> |
|
50 |
+ <string name="pref_header_data_sync">Data & sync</string> |
|
51 |
+ |
|
52 |
+ <string name="pref_title_sync_frequency">Sync frequency</string> |
|
53 |
+ <string-array name="pref_sync_frequency_titles"> |
|
54 |
+ <item>15 minutes</item> |
|
55 |
+ <item>30 minutes</item> |
|
56 |
+ <item>1 hour</item> |
|
57 |
+ <item>3 hours</item> |
|
58 |
+ <item>6 hours</item> |
|
59 |
+ <item>Never</item> |
|
60 |
+ </string-array> |
|
61 |
+ <string-array name="pref_sync_frequency_values"> |
|
62 |
+ <item>15</item> |
|
63 |
+ <item>30</item> |
|
64 |
+ <item>60</item> |
|
65 |
+ <item>180</item> |
|
66 |
+ <item>360</item> |
|
67 |
+ <item>-1</item> |
|
68 |
+ </string-array> |
|
69 |
+ |
|
70 |
+ <string-array name="list_preference_entries"> |
|
71 |
+ <item>Entry 1</item> |
|
72 |
+ <item>Entry 2</item> |
|
73 |
+ <item>Entry 3</item> |
|
74 |
+ </string-array> |
|
75 |
+ |
|
76 |
+ <string-array name="list_preference_entry_values"> |
|
77 |
+ <item>1</item> |
|
78 |
+ <item>2</item> |
|
79 |
+ <item>3</item> |
|
80 |
+ </string-array> |
|
81 |
+ |
|
82 |
+ <string-array name="multi_select_list_preference_default_value" /> |
|
83 |
+ |
|
84 |
+ <string name="pref_title_system_sync_settings">System sync settings</string> |
|
85 |
+ |
|
86 |
+ <!-- Example settings for Notifications --> |
|
87 |
+ <string name="pref_header_notifications">Notifications</string> |
|
88 |
+ |
|
89 |
+ <string name="pref_title_new_message_notifications">New message notifications</string> |
|
90 |
+ |
|
91 |
+ <string name="pref_title_ringtone">Ringtone</string> |
|
92 |
+ <string name="pref_ringtone_silent">Silent</string> |
|
93 |
+ |
|
94 |
+ <string name="pref_title_vibrate">Vibrate</string> |
|
95 |
+ <string name="action_settings">Settings</string> |
|
96 |
+</resources> |
... | ... |
@@ -0,0 +1,20 @@ |
1 |
+<resources> |
|
2 |
+ |
|
3 |
+ <!-- Base application theme. --> |
|
4 |
+ <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> |
|
5 |
+ <!-- Customize your theme here. --> |
|
6 |
+ <item name="colorPrimary">@color/colorPrimary</item> |
|
7 |
+ <item name="colorPrimaryDark">@color/colorPrimaryDark</item> |
|
8 |
+ <item name="colorAccent">@color/colorAccent</item> |
|
9 |
+ </style> |
|
10 |
+ |
|
11 |
+ <style name="AppTheme.NoActionBar"> |
|
12 |
+ <item name="windowActionBar">false</item> |
|
13 |
+ <item name="windowNoTitle">true</item> |
|
14 |
+ </style> |
|
15 |
+ |
|
16 |
+ <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" /> |
|
17 |
+ |
|
18 |
+ <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" /> |
|
19 |
+ |
|
20 |
+</resources> |
... | ... |
@@ -0,0 +1,52 @@ |
1 |
+<?xml version="1.0" encoding="utf-8"?> |
|
2 |
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> |
|
3 |
+ |
|
4 |
+ <PreferenceCategory android:title="@string/server_settings_label"> |
|
5 |
+ |
|
6 |
+ <EditTextPreference |
|
7 |
+ android:id="@+id/server_url" |
|
8 |
+ android:defaultValue="@string/default_server_url" |
|
9 |
+ android:key="server_url" |
|
10 |
+ android:selectAllOnFocus="true" |
|
11 |
+ android:singleLine="true" |
|
12 |
+ android:summary="@string/server_url_description" |
|
13 |
+ android:title="@string/server_url_label" /> |
|
14 |
+ <SwitchPreference |
|
15 |
+ android:defaultValue="true" |
|
16 |
+ android:key="automatically_refresh" |
|
17 |
+ android:summary="@string/auto_refresh_description" |
|
18 |
+ android:title="@string/auto_refresh_label" /> |
|
19 |
+ <ListPreference |
|
20 |
+ android:defaultValue="5" |
|
21 |
+ android:dependency="automatically_refresh" |
|
22 |
+ android:entries="@array/refresh_rates" |
|
23 |
+ android:entryValues="@array/refresh_values" |
|
24 |
+ android:key="refresh_preference" |
|
25 |
+ android:summary="@string/refresh_rate_description" |
|
26 |
+ android:title="@string/refresh_rate_label" /> |
|
27 |
+ </PreferenceCategory> |
|
28 |
+ <PreferenceCategory android:title="@string/notification_preferences_label"> |
|
29 |
+ |
|
30 |
+ <SwitchPreference |
|
31 |
+ android:id="@+id/notifications_enabled" |
|
32 |
+ android:defaultValue="true" |
|
33 |
+ android:key="notifications_enabled" |
|
34 |
+ android:summary="@string/notifications_enabled_description" |
|
35 |
+ android:title="@string/notifications_enabled_label" /> |
|
36 |
+ <RingtonePreference |
|
37 |
+ android:defaultValue="" |
|
38 |
+ android:dependency="notifications_enabled" |
|
39 |
+ android:key="notification_sound" |
|
40 |
+ android:ringtoneType="notification|alarm" |
|
41 |
+ android:title="@string/notification_sound_label" /> |
|
42 |
+ </PreferenceCategory> |
|
43 |
+ <PreferenceCategory android:title="@string/debug_label"> |
|
44 |
+ |
|
45 |
+ <SwitchPreference |
|
46 |
+ android:id="@+id/debug" |
|
47 |
+ android:defaultValue="false" |
|
48 |
+ android:key="debug" |
|
49 |
+ android:summary="@string/debug_mode_description" |
|
50 |
+ android:title="@string/debug_mode_label" /> |
|
51 |
+ </PreferenceCategory> |
|
52 |
+</PreferenceScreen> |
... | ... |
@@ -0,0 +1,17 @@ |
1 |
+package app.librenews.io.librenews; |
|
2 |
+ |
|
3 |
+import org.junit.Test; |
|
4 |
+ |
|
5 |
+import static org.junit.Assert.*; |
|
6 |
+ |
|
7 |
+/** |
|
8 |
+ * Example local unit test, which will execute on the development machine (host). |
|
9 |
+ * |
|
10 |
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> |
|
11 |
+ */ |
|
12 |
+public class ExampleUnitTest { |
|
13 |
+ @Test |
|
14 |
+ public void addition_isCorrect() throws Exception { |
|
15 |
+ assertEquals(4, 2 + 2); |
|
16 |
+ } |
|
17 |
+} |
... | ... |
@@ -0,0 +1,23 @@ |
1 |
+// Top-level build file where you can add configuration options common to all sub-projects/modules. |
|
2 |
+ |
|
3 |
+buildscript { |
|
4 |
+ repositories { |
|
5 |
+ jcenter() |
|
6 |
+ } |
|
7 |
+ dependencies { |
|
8 |
+ classpath 'com.android.tools.build:gradle:2.3.3' |
|
9 |
+ |
|
10 |
+ // NOTE: Do not place your application dependencies here; they belong |
|
11 |
+ // in the individual module build.gradle files |
|
12 |
+ } |
|
13 |
+} |
|
14 |
+ |
|
15 |
+allprojects { |
|
16 |
+ repositories { |
|
17 |
+ jcenter() |
|
18 |
+ } |
|
19 |
+} |
|
20 |
+ |
|
21 |
+task clean(type: Delete) { |
|
22 |
+ delete rootProject.buildDir |
|
23 |
+} |
... | ... |
@@ -0,0 +1,17 @@ |
1 |
+# Project-wide Gradle settings. |
|
2 |
+ |
|
3 |
+# IDE (e.g. Android Studio) users: |
|
4 |
+# Gradle settings configured through the IDE *will override* |
|
5 |
+# any settings specified in this file. |
|
6 |
+ |
|
7 |
+# For more details on how to configure your build environment visit |
|
8 |
+# http://www.gradle.org/docs/current/userguide/build_environment.html |
|
9 |
+ |
|
10 |
+# Specifies the JVM arguments used for the daemon process. |
|
11 |
+# The setting is particularly useful for tweaking memory settings. |
|
12 |
+org.gradle.jvmargs=-Xmx1536m |
|
13 |
+ |
|
14 |
+# When configured, Gradle will run in incubating parallel mode. |
|
15 |
+# This option should only be used with decoupled projects. More details, visit |
|
16 |
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects |
|
17 |
+# org.gradle.parallel=true |
... | ... |
@@ -0,0 +1,6 @@ |
1 |
+#Fri Jul 14 22:41:16 EDT 2017 |
|
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-3.3-all.zip |
... | ... |
@@ -0,0 +1,160 @@ |
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 |
+# Attempt to set APP_HOME |
|
46 |
+# Resolve links: $0 may be a link |
|
47 |
+PRG="$0" |
|
48 |
+# Need this for relative symlinks. |
|
49 |
+while [ -h "$PRG" ] ; do |
|
50 |
+ ls=`ls -ld "$PRG"` |
|
51 |
+ link=`expr "$ls" : '.*-> \(.*\)$'` |
|
52 |
+ if expr "$link" : '/.*' > /dev/null; then |
|
53 |
+ PRG="$link" |
|
54 |
+ else |
|
55 |
+ PRG=`dirname "$PRG"`"/$link" |
|
56 |
+ fi |
|
57 |
+done |
|
58 |
+SAVED="`pwd`" |
|
59 |
+cd "`dirname \"$PRG\"`/" >/dev/null |
|
60 |
+APP_HOME="`pwd -P`" |
|
61 |
+cd "$SAVED" >/dev/null |
|
62 |
+ |
|
63 |
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar |
|
64 |
+ |
|
65 |
+# Determine the Java command to use to start the JVM. |
|
66 |
+if [ -n "$JAVA_HOME" ] ; then |
|
67 |
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then |
|
68 |
+ # IBM's JDK on AIX uses strange locations for the executables |
|
69 |
+ JAVACMD="$JAVA_HOME/jre/sh/java" |
|
70 |
+ else |
|
71 |
+ JAVACMD="$JAVA_HOME/bin/java" |
|
72 |
+ fi |
|
73 |
+ if [ ! -x "$JAVACMD" ] ; then |
|
74 |
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME |
|
75 |
+ |
|
76 |
+Please set the JAVA_HOME variable in your environment to match the |
|
77 |
+location of your Java installation." |
|
78 |
+ fi |
|
79 |
+else |
|
80 |
+ JAVACMD="java" |
|
81 |
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. |
|
82 |
+ |
|
83 |
+Please set the JAVA_HOME variable in your environment to match the |
|
84 |
+location of your Java installation." |
|
85 |
+fi |
|
86 |
+ |
|
87 |
+# Increase the maximum file descriptors if we can. |
|
88 |
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then |
|
89 |
+ MAX_FD_LIMIT=`ulimit -H -n` |
|
90 |
+ if [ $? -eq 0 ] ; then |
|
91 |
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then |
|
92 |
+ MAX_FD="$MAX_FD_LIMIT" |
|
93 |
+ fi |
|
94 |
+ ulimit -n $MAX_FD |
|
95 |
+ if [ $? -ne 0 ] ; then |
|
96 |
+ warn "Could not set maximum file descriptor limit: $MAX_FD" |
|
97 |
+ fi |
|
98 |
+ else |
|
99 |
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" |
|
100 |
+ fi |
|
101 |
+fi |
|
102 |
+ |
|
103 |
+# For Darwin, add options to specify how the application appears in the dock |
|
104 |
+if $darwin; then |
|
105 |
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" |
|
106 |
+fi |
|
107 |
+ |
|
108 |
+# For Cygwin, switch paths to Windows format before running java |
|
109 |
+if $cygwin ; then |
|
110 |
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"` |
|
111 |
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` |
|
112 |
+ JAVACMD=`cygpath --unix "$JAVACMD"` |
|
113 |
+ |
|
114 |
+ # We build the pattern for arguments to be converted via cygpath |
|
115 |
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` |
|
116 |
+ SEP="" |
|
117 |
+ for dir in $ROOTDIRSRAW ; do |
|
118 |
+ ROOTDIRS="$ROOTDIRS$SEP$dir" |
|
119 |
+ SEP="|" |
|
120 |
+ done |
|
121 |
+ OURCYGPATTERN="(^($ROOTDIRS))" |
|
122 |
+ # Add a user-defined pattern to the cygpath arguments |
|
123 |
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then |
|
124 |
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" |
|
125 |
+ fi |
|
126 |
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh |
|
127 |
+ i=0 |
|
128 |
+ for arg in "$@" ; do |
|
129 |
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` |
|
130 |
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option |
|
131 |
+ |
|
132 |
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition |
|
133 |
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` |
|
134 |
+ else |
|
135 |
+ eval `echo args$i`="\"$arg\"" |
|
136 |
+ fi |
|
137 |
+ i=$((i+1)) |
|
138 |
+ done |
|
139 |
+ case $i in |
|
140 |
+ (0) set -- ;; |
|
141 |
+ (1) set -- "$args0" ;; |
|
142 |
+ (2) set -- "$args0" "$args1" ;; |
|
143 |
+ (3) set -- "$args0" "$args1" "$args2" ;; |
|
144 |
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;; |
|
145 |
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; |
|
146 |
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; |
|
147 |
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; |
|
148 |
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; |
|
149 |
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; |
|
150 |
+ esac |
|
151 |
+fi |
|
152 |
+ |
|
153 |
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules |
|
154 |
+function splitJvmOpts() { |
|
155 |
+ JVM_OPTS=("$@") |
|
156 |
+} |
|
157 |
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS |
|
158 |
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" |
|
159 |
+ |
|
160 |
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" |
... | ... |
@@ -0,0 +1,90 @@ |
1 |
+@if "%DEBUG%" == "" @echo off |
|
2 |
+@rem ########################################################################## |
|
3 |
+@rem |
|
4 |
+@rem Gradle startup script for Windows |
|
5 |
+@rem |
|
6 |
+@rem ########################################################################## |
|
7 |
+ |
|
8 |
+@rem Set local scope for the variables with windows NT shell |
|
9 |
+if "%OS%"=="Windows_NT" setlocal |
|
10 |
+ |
|
11 |
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. |
|
12 |
+set DEFAULT_JVM_OPTS= |
|
13 |
+ |
|
14 |
+set DIRNAME=%~dp0 |
|
15 |
+if "%DIRNAME%" == "" set DIRNAME=. |
|
16 |
+set APP_BASE_NAME=%~n0 |
|
17 |
+set APP_HOME=%DIRNAME% |
|
18 |
+ |
|
19 |
+@rem Find java.exe |
|
20 |
+if defined JAVA_HOME goto findJavaFromJavaHome |
|
21 |
+ |
|
22 |
+set JAVA_EXE=java.exe |
|
23 |
+%JAVA_EXE% -version >NUL 2>&1 |
|
24 |
+if "%ERRORLEVEL%" == "0" goto init |
|
25 |
+ |
|
26 |
+echo. |
|
27 |
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. |
|
28 |
+echo. |
|
29 |
+echo Please set the JAVA_HOME variable in your environment to match the |
|
30 |
+echo location of your Java installation. |
|
31 |
+ |
|
32 |
+goto fail |
|
33 |
+ |
|
34 |
+:findJavaFromJavaHome |
|
35 |
+set JAVA_HOME=%JAVA_HOME:"=% |
|
36 |
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe |
|
37 |
+ |
|
38 |
+if exist "%JAVA_EXE%" goto init |
|
39 |
+ |
|
40 |
+echo. |
|
41 |
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% |
|
42 |
+echo. |
|
43 |
+echo Please set the JAVA_HOME variable in your environment to match the |
|
44 |
+echo location of your Java installation. |
|
45 |
+ |
|
46 |
+goto fail |
|
47 |
+ |
|
48 |
+:init |
|
49 |
+@rem Get command-line arguments, handling Windowz variants |
|
50 |
+ |
|
51 |
+if not "%OS%" == "Windows_NT" goto win9xME_args |
|
52 |
+if "%@eval[2+2]" == "4" goto 4NT_args |
|
53 |
+ |
|
54 |
+:win9xME_args |
|
55 |
+@rem Slurp the command line arguments. |
|
56 |
+set CMD_LINE_ARGS= |
|
57 |
+set _SKIP=2 |
|
58 |
+ |
|
59 |
+:win9xME_args_slurp |
|
60 |
+if "x%~1" == "x" goto execute |
|
61 |
+ |
|
62 |
+set CMD_LINE_ARGS=%* |
|
63 |
+goto execute |
|
64 |
+ |
|
65 |
+:4NT_args |
|
66 |
+@rem Get arguments from the 4NT Shell from JP Software |
|
67 |
+set CMD_LINE_ARGS=%$ |
|
68 |
+ |
|
69 |
+:execute |
|
70 |
+@rem Setup the command line |
|
71 |
+ |
|
72 |
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar |
|
73 |
+ |
|
74 |
+@rem Execute Gradle |
|
75 |
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% |
|
76 |
+ |
|
77 |
+:end |
|
78 |
+@rem End local scope for the variables with windows NT shell |
|
79 |
+if "%ERRORLEVEL%"=="0" goto mainEnd |
|
80 |
+ |
|
81 |
+:fail |
|
82 |
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of |
|
83 |
+rem the _cmd.exe /c_ return code! |
|
84 |
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 |
|
85 |
+exit /b 1 |
|
86 |
+ |
|
87 |
+:mainEnd |
|
88 |
+if "%OS%"=="Windows_NT" endlocal |
|
89 |
+ |
|
90 |
+:omega |
... | ... |
@@ -0,0 +1 @@ |
1 |
+include ':LibreNews-App' |