├── LICENSE
├── README.md
├── android
├── .gitignore
├── DashboardRedux
│ ├── DashboardRedux.iml
│ ├── build.gradle
│ ├── libs
│ │ ├── README.txt
│ │ └── RootTools-3.4.jar
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── assets
│ │ ├── README.txt
│ │ └── dashboard.pbw
│ │ ├── java
│ │ ├── activity
│ │ │ ├── AboutActivity.java
│ │ │ ├── Landing.java
│ │ │ └── LogViewer.java
│ │ ├── adapter
│ │ │ └── ToggleSelector.java
│ │ ├── background
│ │ │ ├── App.java
│ │ │ ├── BootPackageChangeReceiver.java
│ │ │ ├── DeviceAdmin.java
│ │ │ ├── FindPhone.java
│ │ │ ├── HandlerService.java
│ │ │ ├── KeepAliveReceiver.java
│ │ │ ├── PSLCallback.java
│ │ │ ├── PebbleReceiver.java
│ │ │ └── SignalListener.java
│ │ ├── cl_toolkit
│ │ │ ├── Contact.java
│ │ │ ├── FileMap.java
│ │ │ ├── Logger.java
│ │ │ ├── Meta.java
│ │ │ ├── Platform.java
│ │ │ ├── Radios.java
│ │ │ ├── Root.java
│ │ │ ├── Storage.java
│ │ │ ├── UserInterface.java
│ │ │ └── Web.java
│ │ ├── config
│ │ │ ├── Build.java
│ │ │ ├── Keys.java
│ │ │ └── Runtime.java
│ │ ├── fragment
│ │ │ └── About.java
│ │ └── util
│ │ │ ├── PebbleUtils.java
│ │ │ └── VersionCheck.java
│ │ └── res
│ │ ├── anim
│ │ └── slide_in_bottom.xml
│ │ ├── drawable-hdpi
│ │ └── ic_launcher_notif.png
│ │ ├── drawable-mdpi
│ │ └── ic_launcher_notif.png
│ │ ├── drawable-xhdpi
│ │ ├── ap.png
│ │ ├── brightness.png
│ │ ├── bt.png
│ │ ├── button.9.png
│ │ ├── data.png
│ │ ├── empty.png
│ │ ├── framed.png
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_notif.png
│ │ ├── ic_launcher_notif_large.png
│ │ ├── lock.png
│ │ ├── loud.png
│ │ ├── mailsrc.png
│ │ ├── paypal.png
│ │ ├── phone.png
│ │ ├── priority.png
│ │ ├── questionmark.png
│ │ ├── rate.png
│ │ ├── silent.png
│ │ ├── sync.png
│ │ ├── tweet.png
│ │ ├── vibrate.png
│ │ ├── wifi.png
│ │ └── wordpress.png
│ │ ├── layout
│ │ ├── activity_landing.xml
│ │ ├── activity_log_viewer.xml
│ │ ├── settings_fragment.xml
│ │ └── toggle_spinner_item.xml
│ │ ├── menu
│ │ ├── menu_landing.xml
│ │ └── menu_log_viewer.xml
│ │ ├── values-v11
│ │ └── styles.xml
│ │ ├── values-v14
│ │ └── styles.xml
│ │ ├── values-v21
│ │ └── styles.xml
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ ├── values
│ │ ├── colours.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ │ └── xml
│ │ ├── actionbar_tab_indicator.xml
│ │ ├── admin.xml
│ │ └── preferences.xml
├── PebbleHomescreen.iml
├── android.iml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
├── assets
├── listing
│ ├── app_logo.png
│ ├── blogbanner.png
│ ├── icon-large.png
│ ├── icon-small.png
│ ├── icons-original-size
│ │ ├── ap.png
│ │ ├── brightness.png
│ │ ├── bt.png
│ │ ├── data.png
│ │ ├── lock.png
│ │ ├── loud.png
│ │ ├── phone.png
│ │ ├── priority.png
│ │ ├── silent.png
│ │ ├── sync.png
│ │ ├── vibrate.png
│ │ └── wifi.png
│ ├── marketing-aplite.png
│ ├── marketing-basalt.png
│ ├── marketing-chalk.png
│ ├── marketing-gplay.png
│ ├── marketing-src-aplite.xcf
│ ├── marketing-src-basalt.xcf
│ ├── marketing-src-chalk.xcf
│ └── marketing-src-gplay.xcf
├── releases
│ ├── Dashboard-release-1.13.apk
│ ├── Dashboard-release-1.14.apk
│ ├── Dashboard-release-1.15.apk
│ ├── Dashboard-release-1.16.apk
│ ├── Dashboard-release-1.17.apk
│ ├── Dashboard-release-1.19.apk
│ ├── Dashboard-release-1.20.apk
│ ├── Dashboard-release-3.0.apk
│ ├── Dashboard-release-3.1.apk
│ ├── Dashboard-release-3.2.apk
│ ├── Dashboard-release-4.0.apk
│ ├── Dashboard-release-4.1.apk
│ ├── Dashboard-release-4.10.apk
│ ├── Dashboard-release-4.11.apk
│ ├── Dashboard-release-4.12.apk
│ ├── Dashboard-release-4.14.apk
│ ├── Dashboard-release-4.15.apk
│ ├── Dashboard-release-4.16.apk
│ ├── Dashboard-release-4.17.apk
│ ├── Dashboard-release-4.2.apk
│ ├── Dashboard-release-4.3.apk
│ ├── Dashboard-release-4.4.apk
│ ├── Dashboard-release-4.5.apk
│ ├── Dashboard-release-4.6.apk
│ ├── Dashboard-release-4.7.apk
│ ├── Dashboard-release-4.8.apk
│ ├── Dashboard-release-4.9.apk
│ ├── dashboard-4.0.pbw
│ ├── dashboard-4.1.pbw
│ ├── dashboard-4.2.pbw
│ ├── dashboard-4.3.pbw
│ ├── dashboard-4.5.pbw
│ ├── dashboard-4.6.pbw
│ └── dashboard-4.8.pbw
└── screenshots
│ ├── Screenshot_20160504-164142.png
│ ├── Screenshot_20160504-164150.png
│ ├── aplite1.png
│ ├── aplite2.png
│ ├── aplite3.png
│ ├── aplite4.png
│ ├── basalt1.png
│ ├── basalt2.png
│ ├── basalt3.png
│ ├── basalt4.png
│ ├── chalk1.png
│ ├── chalk2.png
│ ├── chalk3.png
│ └── chalk4.png
└── pebble
├── package.json
├── resources
├── icons
│ ├── ap.png
│ ├── brightness.png
│ ├── bt.png
│ ├── data.png
│ ├── lock.png
│ ├── loud.png
│ ├── phone.png
│ ├── priority.png
│ ├── silent.png
│ ├── sync.png
│ ├── vibrate.png
│ └── wifi.png
└── images
│ ├── app_icon.png
│ ├── down.png
│ ├── logo.png
│ ├── select.png
│ ├── tick.png
│ └── up.png
├── src
└── c
│ ├── config.h
│ ├── lib
│ └── new_number_window
│ │ ├── new_number_window.c
│ │ └── new_number_window.h
│ ├── main.c
│ ├── modules
│ ├── comm.c
│ ├── comm.h
│ ├── data.c
│ ├── data.h
│ ├── icons.c
│ ├── icons.h
│ ├── sync_timer.c
│ └── sync_timer.h
│ ├── types.h
│ ├── util
│ ├── animation.c
│ ├── animation.h
│ ├── test.c
│ └── test.h
│ └── windows
│ ├── dialog_window.c
│ ├── dialog_window.h
│ ├── list_window.c
│ ├── list_window.h
│ ├── schedule_window.c
│ ├── schedule_window.h
│ ├── splash_window.c
│ ├── splash_window.h
│ ├── unified_window.c
│ ├── unified_window.h
│ ├── wakeup_window.c
│ └── wakeup_window.h
└── wscript
/LICENSE:
--------------------------------------------------------------------------------
1 | (The MIT License)
2 |
3 | Copyright (c) 2016 Chris Lewis
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | 'Software'), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # dashboard
2 |
3 | Source code for the Dashboard app. Includes code for both the Pebble watchapp and the Android companion app.
4 |
5 | See `assets/releases` for sideloadable Android and Pebble apps files.
6 |
7 | **Latest Versions**
8 |
9 | - Watchapp: 4.8
10 |
11 | - Android app: 4.16
12 |
13 |
14 | **Install**
15 |
16 | - [Appstore](https://apps.rebble.io/en_US/application/53ec8d840c3036447e000109)
17 |
18 | - [Google Play](https://play.google.com/store/apps/details?id=com.wordpress.ninedof.dashboard)
19 |
20 |
21 | > Don't try this at home! See [Dashboard API Status](https://github.com/C-D-Lewis/dashboard-api-status).
22 |
23 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | /.idea/workspace.xml
4 | .DS_Store
5 | /.idea
6 | **/build/
--------------------------------------------------------------------------------
/android/DashboardRedux/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 29
5 |
6 | defaultConfig {
7 | applicationId "com.wordpress.ninedof.dashboard"
8 | minSdkVersion 18
9 | targetSdkVersion 29
10 | }
11 |
12 | buildTypes {
13 | release {
14 | minifyEnabled false
15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
16 | }
17 | }
18 | sourceSets { main { java.srcDirs = ['src/main/java', 'src/main/java/receiver'] } }
19 | }
20 |
21 | dependencies {
22 | implementation 'androidx.legacy:legacy-support-v4:1.0.0'
23 | implementation 'androidx.cardview:cardview:1.0.0'
24 | implementation 'com.google.android.gms:play-services-location:17.1.0'
25 | implementation 'com.getpebble:pebblekit:3.0.0'
26 | compile files('libs/RootTools-3.4.jar')
27 | }
28 |
--------------------------------------------------------------------------------
/android/DashboardRedux/libs/README.txt:
--------------------------------------------------------------------------------
1 | Stericson RootTools repo: https://github.com/Stericson/RootTools
2 |
--------------------------------------------------------------------------------
/android/DashboardRedux/libs/RootTools-3.4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/libs/RootTools-3.4.jar
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
51 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
65 |
66 |
67 |
68 |
69 |
72 |
73 |
74 |
75 |
76 |
77 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
100 |
101 |
102 |
103 |
104 |
107 |
108 |
109 |
110 |
111 |
112 |
115 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/assets/README.txt:
--------------------------------------------------------------------------------
1 | Make sure this is the latest compatible appstore version!
2 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/assets/dashboard.pbw:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/assets/dashboard.pbw
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/activity/AboutActivity.java:
--------------------------------------------------------------------------------
1 | package activity;
2 |
3 | import android.app.ActionBar;
4 | import android.app.Activity;
5 | import android.graphics.drawable.ColorDrawable;
6 | import android.os.Bundle;
7 |
8 | import com.wordpress.ninedof.dashboard.R;
9 |
10 | import fragment.About;
11 |
12 | public class AboutActivity extends Activity {
13 |
14 | @Override
15 | protected void onCreate(Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | setContentView(R.layout.settings_fragment);
18 |
19 | ActionBar ab = getActionBar();
20 | ab.setTitle("About Dashboard");
21 | ab.setBackgroundDrawable(new ColorDrawable(getResources().getColor(R.color.main_colour)));
22 | ab.setHomeButtonEnabled(true);
23 | }
24 |
25 | @Override
26 | protected void onResume() {
27 | super.onResume();
28 |
29 | getFragmentManager()
30 | .beginTransaction()
31 | .replace(R.id.fragment_layout, new fragment.About())
32 | .commit();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/activity/LogViewer.java:
--------------------------------------------------------------------------------
1 | package activity;
2 |
3 | import android.app.ActionBar;
4 | import android.app.Activity;
5 | import android.content.Context;
6 | import android.content.DialogInterface;
7 | import android.content.Intent;
8 | import android.graphics.drawable.ColorDrawable;
9 | import android.net.Uri;
10 | import android.os.Bundle;
11 | import android.util.Log;
12 | import android.view.Menu;
13 | import android.view.MenuItem;
14 | import android.view.View;
15 | import android.widget.ScrollView;
16 | import android.widget.TextView;
17 | import android.widget.Toast;
18 |
19 | import com.wordpress.ninedof.dashboard.R;
20 |
21 | import java.io.BufferedReader;
22 | import java.io.File;
23 | import java.io.FileReader;
24 | import java.io.IOException;
25 | import java.text.SimpleDateFormat;
26 | import java.util.Calendar;
27 | import java.util.Timer;
28 | import java.util.TimerTask;
29 |
30 | import cl_toolkit.Logger;
31 | import cl_toolkit.Storage;
32 | import cl_toolkit.UserInterface;
33 | import config.Build;
34 | import config.Runtime;
35 |
36 | public class LogViewer extends Activity {
37 |
38 | private static final String TAG = LogViewer.class.getName();
39 |
40 | private ActionBar actionBar;
41 | private TextView textView;
42 | private ScrollView scrollView;
43 |
44 | @Override
45 | protected void onCreate(Bundle savedInstanceState) {
46 | super.onCreate(savedInstanceState);
47 | setContentView(R.layout.activity_log_viewer);
48 |
49 | //Setup ActionBar
50 | actionBar = getActionBar();
51 | actionBar.setTitle("Debug Log");
52 | actionBar.setBackgroundDrawable(new ColorDrawable(getResources().getColor(R.color.main_colour)));
53 |
54 | // Get UI members
55 | textView = (TextView)findViewById(R.id.log_view);
56 | scrollView = (ScrollView)findViewById(R.id.scroll_view);
57 |
58 | //Fill view
59 | refreshLog();
60 | beginTimer();
61 | }
62 |
63 | private void beginTimer() {
64 | Timer timer = new Timer();
65 | timer.scheduleAtFixedRate(new TimerTask() {
66 |
67 | @Override
68 | public void run() {
69 | runOnUiThread(new Runnable() {
70 |
71 | @Override
72 | public void run() {
73 | SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
74 | String time = format.format(Calendar.getInstance().getTime());
75 | actionBar.setSubtitle(time);
76 | }
77 |
78 | });
79 | }
80 |
81 | }, 0, 1000);
82 | }
83 |
84 | private void refreshLog() {
85 | new Thread(new Runnable() {
86 |
87 | @Override
88 | public void run() {
89 | Context context = getApplicationContext();
90 |
91 | try {
92 | readFile();
93 | } catch(Exception e) {
94 | Log.e(TAG, e.getMessage());
95 |
96 | //There may not be one yet
97 | Runtime.log(context, TAG, "Began new log file.", Logger.INFO);
98 | try {
99 | readFile();
100 | } catch(Exception e1) {
101 | Runtime.log(context, TAG, "That REALLY went wrong...", Logger.ERROR);
102 | Runtime.logStackTrace(context, e1);
103 | }
104 | }
105 |
106 | }
107 |
108 | }).start();
109 | }
110 |
111 | private void readFile() throws IOException {
112 | File logFile = new File(Storage.getAppStorage(getApplicationContext()) + "/" + Build.DEBUG_LOG_NAME);
113 | BufferedReader br = new BufferedReader(new FileReader(logFile));
114 | String next = br.readLine();
115 | StringBuilder content = new StringBuilder(Build.DEBUG_LOG_MAX_SIZE_BYTES);
116 | while(next != null) {
117 | content.append(next + "\n");
118 | next = br.readLine();
119 | }
120 | br.close();
121 |
122 | //Show it
123 | final String newString = content.toString();
124 | runOnUiThread(new Runnable() {
125 |
126 | @Override
127 | public void run() {
128 | textView.setText(newString);
129 | scrollView.post(new Runnable() {
130 |
131 | @Override
132 | public void run() {
133 | scrollView.fullScroll(View.FOCUS_DOWN);
134 | }
135 | });
136 | }
137 |
138 | });
139 | }
140 |
141 | @Override
142 | public boolean onCreateOptionsMenu(Menu menu) {
143 | getMenuInflater().inflate(R.menu.menu_log_viewer, menu);
144 | return true;
145 | }
146 |
147 | @Override
148 | public boolean onOptionsItemSelected(MenuItem item) {
149 | switch(item.getItemId()) {
150 | case R.id.action_refresh:
151 | refreshLog();
152 | break;
153 | case R.id.action_report:
154 | UserInterface.showDialog(this,
155 | "Log Reporting",
156 | "You are about to send a copy of this log to the developer to help fix a problem.\n\nPlease include a description of the problem you are experiencing.\n\nThanks for your co-operation!",
157 | "Send Log",
158 | new DialogInterface.OnClickListener() {
159 |
160 | @Override
161 | public void onClick(DialogInterface dialog, int which) {
162 | sendDevMail();
163 | }
164 | },
165 | "Cancel",
166 | new DialogInterface.OnClickListener() {
167 |
168 | @Override
169 | public void onClick(DialogInterface dialog, int which) {
170 | dialog.dismiss();
171 | }
172 | }
173 | );
174 | break;
175 | }
176 |
177 | //Finally
178 | return super.onOptionsItemSelected(item);
179 | }
180 |
181 | private void sendDevMail() {
182 | //Use the actual one
183 | File attachment = new File(Storage.getStorage() + "/" + Build.DEBUG_LOG_NAME);
184 |
185 | //Open email with attachment
186 | if(attachment != null && attachment.canRead()) {
187 | Intent emailIntent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto","bonsitm@gmail.com", null));
188 | emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Dashboard Debug Log File");
189 | emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(attachment));
190 | startActivity(Intent.createChooser(emailIntent, "Send email..."));
191 | } else {
192 | Toast.makeText(this, "Could not add attachment.", Toast.LENGTH_LONG).show();
193 | }
194 | }
195 |
196 | }
197 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/adapter/ToggleSelector.java:
--------------------------------------------------------------------------------
1 | package adapter;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.ArrayAdapter;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 |
11 | import com.wordpress.ninedof.dashboard.R;
12 |
13 | public class ToggleSelector extends ArrayAdapter {
14 |
15 | public static final String[] items = {
16 | "Wi-Fi",
17 | "Data",
18 | "BT",
19 | "Ringer",
20 | "Auto Sync",
21 | "Hotspot",
22 | "Find Phone",
23 | "Lock Phone",
24 | "Auto Brightness"
25 | };
26 |
27 | public static final int[] iconIds = {
28 | R.drawable.wifi,
29 | R.drawable.data,
30 | R.drawable.bt,
31 | R.drawable.loud,
32 | R.drawable.sync,
33 | R.drawable.ap,
34 | R.drawable.phone,
35 | R.drawable.lock,
36 | R.drawable.brightness
37 | };
38 |
39 | private Context context;
40 |
41 | public ToggleSelector(Context context) {
42 | super(context, R.layout.toggle_spinner_item, items);
43 |
44 | this.context = context;
45 | }
46 |
47 | @Override
48 | public View getDropDownView(int position, View convertView, ViewGroup parent) {
49 | return inflateView(position, parent);
50 | }
51 |
52 | @Override
53 | public View getView(int position, View convertView, ViewGroup parent) {
54 | return inflateView(position, parent);
55 | }
56 |
57 | public View inflateView(int position, ViewGroup parent) {
58 | //Inflate view
59 | LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
60 | View rootView = inflater.inflate(R.layout.toggle_spinner_item, parent, false);
61 |
62 | //Get items
63 | ImageView icon = (ImageView)rootView.findViewById(R.id.icon);
64 | TextView name = (TextView)rootView.findViewById(R.id.name);
65 |
66 | icon.setImageResource(iconIds[position]);
67 | name.setText(items[position]);
68 | return rootView;
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/background/App.java:
--------------------------------------------------------------------------------
1 | package background;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.SharedPreferences;
7 | import android.preference.PreferenceManager;
8 |
9 | import cl_toolkit.Logger;
10 | import config.Build;
11 | import config.Keys;
12 | import config.Runtime;
13 |
14 | public class App extends Application {
15 |
16 | private static final String TAG = App.class.getName();
17 |
18 | @Override
19 | public void onCreate() {
20 | super.onCreate();
21 | Context context = getApplicationContext();
22 |
23 | if (!Build.RELEASE) {
24 | Runtime.log(context, TAG, "Application onCreate", Logger.DEBUG);
25 | }
26 |
27 | // Start KeepAlive alarms here, since on first install we won't get PACKAGE_ADDED intent broadcast, and it won't be boot time
28 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
29 | if(prefs.getBoolean(Keys.PREF_FIRST_RUN, true)) {
30 | Intent i = new Intent(context, BootPackageChangeReceiver.class);
31 | i.setAction(BootPackageChangeReceiver.ACTION_START_BATTERY_MONITORING);
32 | sendBroadcast(i);
33 |
34 | // DON'T CLEAR FIRST RUN FLAG, Landing.java will do this
35 | }
36 |
37 | PebbleReceiver.registerReceiver(context);
38 | }
39 |
40 | @Override
41 | public void onTerminate() {
42 | Context context = getApplicationContext();
43 | PebbleReceiver.unregisterReceiver(context);
44 |
45 | super.onTerminate();
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/background/BootPackageChangeReceiver.java:
--------------------------------------------------------------------------------
1 | package background;
2 |
3 | import android.app.AlarmManager;
4 | import android.app.PendingIntent;
5 | import android.content.BroadcastReceiver;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.SharedPreferences;
9 | import android.preference.PreferenceManager;
10 |
11 | import cl_toolkit.Logger;
12 | import config.Build;
13 | import config.Keys;
14 | import config.Runtime;
15 |
16 | /**
17 | * I really love Android APIs sometimes...
18 | * http://stackoverflow.com/questions/11277302/i-cant-receive-broadcast-on-battery-state-change/11277524#11277524
19 | */
20 | public class BootPackageChangeReceiver extends BroadcastReceiver {
21 |
22 | private static final String TAG = BootPackageChangeReceiver.class.getName();
23 |
24 | public static final String ACTION_START_BATTERY_MONITORING = "com.wordpress.ninedof.dashboard.action.START_BATTERY_MONITORING";
25 |
26 | private static final int
27 | MINS = 30,
28 | REQUEST_CODE = 1;
29 |
30 | private boolean thisApp(Intent intent) {
31 | return intent.getData().getEncodedSchemeSpecificPart().equals(Build.PACKAGE_NAME);
32 | }
33 |
34 | @Override
35 | public void onReceive(Context context, Intent intent) {
36 | // Do our best to be alive for future background usage...
37 | PebbleReceiver.launchHandlerService(context, "");
38 |
39 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
40 | if(!prefs.getBoolean(Keys.PREF_KEY_CHARGE_NOTIFICATION_ENABLED, false)) {
41 | return;
42 | }
43 |
44 | String action = intent.getAction();
45 | Runtime.log(context, TAG, "Starting battery monitor due to...", Logger.INFO); // TODO sometimes this is never followed up
46 |
47 | // Boot completed
48 | if(action.equals(Intent.ACTION_BOOT_COMPLETED)) {
49 | Runtime.log(context, TAG, "Boot completed", Logger.INFO);
50 | }
51 |
52 | // Launched by me
53 | else if(action.equals(ACTION_START_BATTERY_MONITORING)) {
54 | Runtime.log(context, TAG, "Requested by Dashboard", Logger.INFO);
55 | }
56 |
57 | // App updated or installed
58 | else if(action.equals(Intent.ACTION_PACKAGE_REPLACED)
59 | || action.equals(Intent.ACTION_PACKAGE_ADDED)) {
60 | if(!thisApp(intent)) {
61 | return;
62 | }
63 | Runtime.log(context, TAG, "Dashboard package added or replaced", Logger.INFO);
64 | }
65 |
66 | else {
67 | Runtime.log(context, TAG, "Some other reason: " + action, Logger.INFO);
68 | }
69 |
70 | startAlarms(context);
71 | }
72 |
73 | private static PendingIntent getIdenticalPendingIntent(Context context) {
74 | Intent i = new Intent(context, KeepAliveReceiver.class);
75 | return PendingIntent.getBroadcast(context, REQUEST_CODE, i, 0);
76 | }
77 |
78 | public static void startAlarms(Context context) {
79 | clearAlarms(context);
80 |
81 | AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
82 |
83 | PendingIntent pendingIntent = getIdenticalPendingIntent(context);
84 | alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 1000, 1000 * 60 * MINS, pendingIntent); // Millisec * Second * Minute
85 |
86 | Runtime.log(context, TAG, "AM start", Logger.DEBUG);
87 | }
88 |
89 | public static void clearAlarms(Context context) {
90 | AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
91 | PendingIntent pendingIntent = getIdenticalPendingIntent(context);
92 | alarmManager.cancel(pendingIntent); // Works because the pendingIntent is identical to that which was registered
93 |
94 | Runtime.log(context, TAG, "AM cancel", Logger.INFO);
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/background/DeviceAdmin.java:
--------------------------------------------------------------------------------
1 | package background;
2 |
3 | import android.app.admin.DeviceAdminReceiver;
4 |
5 | public class DeviceAdmin extends DeviceAdminReceiver {
6 | // Must have an 'implementation' to use the API, apparently
7 | // But we don't want anything special here
8 | }
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/background/FindPhone.java:
--------------------------------------------------------------------------------
1 | package background;
2 |
3 | import android.app.Notification;
4 | import android.app.NotificationChannel;
5 | import android.app.NotificationManager;
6 | import android.app.Service;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.content.SharedPreferences;
10 | import android.media.AudioManager;
11 | import android.media.MediaPlayer;
12 | import android.media.RingtoneManager;
13 | import android.net.Uri;
14 | import android.os.IBinder;
15 | import android.preference.PreferenceManager;
16 | import androidx.annotation.RequiresApi;
17 | import androidx.core.app.NotificationCompat;
18 | import android.widget.Toast;
19 |
20 | import com.getpebble.android.kit.PebbleKit;
21 | import com.getpebble.android.kit.util.PebbleDictionary;
22 | import com.wordpress.ninedof.dashboard.R;
23 |
24 | import java.io.IOException;
25 |
26 | import cl_toolkit.Logger;
27 | import config.Build;
28 | import config.Keys;
29 | import config.Runtime;
30 |
31 | public class FindPhone extends Service {
32 |
33 | private static final String TAG = FindPhone.class.getName();
34 | private static final int ID_FIND_PHONE = 347;
35 |
36 | private static MediaPlayer mediaPlayer; // Exist forever pls (or at least while the sound is on)
37 | private static int userVolume;
38 |
39 | @Override
40 | public int onStartCommand(Intent intent, int flags, int startId) {
41 | Context context = getApplicationContext();
42 |
43 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
44 | Runtime.log(context, TAG, "FindPhone onStartCommand", Logger.INFO);
45 |
46 | boolean isRunning = prefs.getBoolean(Keys.PREF_FIND_PHONE_RUNNING, false);
47 | if(isRunning) {
48 | Runtime.log(context, TAG, "Stopping find phone...", Logger.INFO);
49 |
50 | //Stop media
51 | final AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
52 | audioManager.setStreamVolume(AudioManager.STREAM_ALARM, userVolume, AudioManager.FLAG_PLAY_SOUND);
53 |
54 | if(mediaPlayer != null) {
55 | mediaPlayer.stop();
56 | mediaPlayer.reset();
57 | } else {
58 | Runtime.log(context, TAG, "mediaPlayer null when trying to stop Find Phone sound!", Logger.ERROR);
59 | }
60 |
61 | PebbleDictionary dict = new PebbleDictionary();
62 | dict.addInt8(Keys.AppKeyToggleFindPhone, (byte) Keys.ToggleStateOff);
63 | PebbleKit.sendDataToPebble(context, Build.WATCH_APP_UUID, dict);
64 |
65 | SharedPreferences.Editor ed = prefs.edit();
66 | ed.putBoolean(Keys.PREF_FIND_PHONE_RUNNING, false);
67 | ed.commit();
68 |
69 | // Stop service
70 | stopSelf();
71 | } else {
72 | Runtime.log(context, TAG, "Starting find phone...", Logger.INFO);
73 | String message = "Playing Find Phone sound. Use Find Phone toggle again to cancel.";
74 | String channelId = "find_phone_notification";
75 |
76 | if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.O) {
77 | createNotificationChannel(channelId, "Find My Phone");
78 | } else {
79 | // Start
80 | NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId);
81 | builder.setSmallIcon(R.drawable.ic_launcher_notif);
82 | builder.setContentTitle("Dashboard");
83 | builder.setStyle(new NotificationCompat.BigTextStyle().bigText(message));
84 | builder.setColor(getResources().getColor(R.color.main_colour));
85 | startForeground(ID_FIND_PHONE, builder.build());
86 | }
87 |
88 | // Choose user's choice or default
89 | String uriString = prefs.getString(Keys.PREF_KEY_FIND_PHONE_FILE, Keys.PREF_VALUE_FIND_PHONE_DEFAULT);
90 | Uri uri;
91 | if(uriString.equals(Keys.PREF_VALUE_FIND_PHONE_DEFAULT)) {
92 | uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
93 | Runtime.log(context, TAG, "Used Notification tone.", Logger.INFO);
94 | Toast.makeText(context, "No file chosen, or was unavailable. Used Notification sound.", Toast.LENGTH_SHORT).show();
95 | } else {
96 | uri = Uri.parse(uriString);
97 | Runtime.log(context, TAG, "Using loaded Uri: " + uriString, Logger.INFO);
98 | }
99 |
100 | playSound(uri);
101 | }
102 |
103 | Runtime.log(context, TAG, "FindPhone onStartCommand finished", Logger.INFO);
104 |
105 | // Finally
106 | return super.onStartCommand(intent, flags, startId);
107 | }
108 |
109 | private void playSound(Uri uri) {
110 | Context context = getApplicationContext();
111 | final AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
112 |
113 | if(mediaPlayer != null) {
114 | // Stop current playback
115 | mediaPlayer.stop();
116 | mediaPlayer.reset();
117 |
118 | // Reset volume
119 | audioManager.setStreamVolume(AudioManager.STREAM_ALARM, userVolume, AudioManager.FLAG_PLAY_SOUND);
120 | } else {
121 | // Create new one
122 | mediaPlayer = new MediaPlayer();
123 | }
124 |
125 | try {
126 | if(!mediaPlayer.isPlaying()) {
127 | // Get current volume
128 | userVolume = audioManager.getStreamVolume(AudioManager.STREAM_ALARM);
129 |
130 | // Boost volume
131 | audioManager.setStreamVolume(AudioManager.STREAM_ALARM, audioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM), AudioManager.FLAG_PLAY_SOUND);
132 |
133 | mediaPlayer.setDataSource(context, uri);
134 | mediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
135 | mediaPlayer.setLooping(true);
136 | mediaPlayer.prepare();
137 | mediaPlayer.start();
138 | Runtime.log(context, TAG, "Sound started.", Logger.INFO);
139 |
140 | PebbleDictionary out = new PebbleDictionary();
141 | out.addInt8(Keys.AppKeyToggleFindPhone, (byte) Keys.ToggleStateOn);
142 | PebbleKit.sendDataToPebble(context, Build.WATCH_APP_UUID, out);
143 |
144 | SharedPreferences.Editor ed = PreferenceManager.getDefaultSharedPreferences(context).edit();
145 | ed.putBoolean(Keys.PREF_FIND_PHONE_RUNNING, true);
146 | ed.commit();
147 |
148 | Runtime.log(context, TAG, "Sound started.", Logger.INFO);
149 | }
150 | } catch (IOException e) {
151 | Toast.makeText(context, "Your Find Phone sound was unavailable. Resetting to default...", Toast.LENGTH_LONG).show();
152 | SharedPreferences.Editor ed = PreferenceManager.getDefaultSharedPreferences(context).edit();
153 | ed.putString(Keys.PREF_KEY_FIND_PHONE_FILE, Keys.PREF_VALUE_FIND_PHONE_DEFAULT);
154 | ed.commit();
155 | }
156 | audioManager.setStreamVolume(AudioManager.STREAM_ALARM, audioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM), AudioManager.FLAG_PLAY_SOUND);
157 | }
158 |
159 | @Override
160 | public IBinder onBind(Intent intent) {
161 | return null;
162 | }
163 |
164 | @RequiresApi(android.os.Build.VERSION_CODES.O)
165 | private String createNotificationChannel(String channelId, String channelName){
166 | NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_NONE);
167 | channel.setLightColor(getResources().getColor(R.color.main_colour));
168 | channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
169 | NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
170 | manager.createNotificationChannel(channel);
171 |
172 | return channelId;
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/background/KeepAliveReceiver.java:
--------------------------------------------------------------------------------
1 | package background;
2 |
3 | import android.app.NotificationManager;
4 | import android.content.BroadcastReceiver;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.IntentFilter;
8 | import android.content.SharedPreferences;
9 | import android.os.BatteryManager;
10 | import android.preference.PreferenceManager;
11 | import androidx.core.app.NotificationCompat;
12 |
13 | import com.wordpress.ninedof.dashboard.R;
14 |
15 | import cl_toolkit.Logger;
16 | import cl_toolkit.Platform;
17 | import config.Build;
18 | import config.Keys;
19 | import config.Runtime;
20 |
21 | /**
22 | * AKA 'how hard can it be to notify when fully charged??'
23 | */
24 | public class KeepAliveReceiver extends BroadcastReceiver {
25 |
26 | private static final String TAG = KeepAliveReceiver.class.getName();
27 |
28 | public static int NOTIFICATION_ID = 543897;
29 |
30 | private static BroadcastReceiver receiver;
31 |
32 | @Override
33 | public void onReceive(Context context, Intent intent) {
34 | if(!Build.RELEASE) {
35 | Runtime.log(context, TAG, "KeepAliveReceiver onReceive()", Logger.DEBUG);
36 | }
37 |
38 | if(receiver == null) {
39 | Runtime.log(context, TAG, "Battery receiver was null, replacing it. Next unregister will fail.", Logger.INFO);
40 | }
41 | registerReceiver(context.getApplicationContext());
42 | }
43 |
44 | public static void registerReceiver(Context context) {
45 | unregisterReceiver(context);
46 | receiver = new BroadcastReceiver() {
47 |
48 | @Override
49 | public void onReceive(final Context context, final Intent batteryStatus) {
50 | handleChange(context, batteryStatus);
51 | }
52 |
53 | };
54 | IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
55 | context.getApplicationContext().registerReceiver(receiver, filter);
56 |
57 | Runtime.log(context, TAG, "Registered new battery level receiver", Logger.INFO);
58 | if(!Build.RELEASE) {
59 | notify(context, "Receiver registered.");
60 | }
61 | }
62 |
63 | public static void unregisterReceiver(Context context) {
64 | if(receiver == null) {
65 | Runtime.log(context, TAG, "Failed to unregister battery receiver - it was null", Logger.INFO);
66 | return;
67 | }
68 |
69 | try {
70 | context.getApplicationContext().unregisterReceiver(receiver);
71 | if(!Build.RELEASE) {
72 | Runtime.log(context, TAG, "Unregistered battery receiver", Logger.INFO);
73 | }
74 | } catch(Exception e) {
75 | Runtime.log(context, TAG, "Exception unregistering receiver - it may have already been unregistered", Logger.ERROR);
76 | Runtime.logStackTrace(context, e);
77 | }
78 | }
79 |
80 | private static boolean isPluggedIn(Intent batteryState) {
81 | int plugged = batteryState.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
82 | return plugged == BatteryManager.BATTERY_PLUGGED_AC || plugged == BatteryManager.BATTERY_PLUGGED_USB;
83 | }
84 |
85 | private static void handleChange(Context c, Intent batteryStatus) {
86 | Context context = c.getApplicationContext();
87 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
88 |
89 | // Get new level
90 | int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
91 | int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
92 | int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
93 | float batteryPct = (float) level / (float) scale;
94 | int currentLevel = Math.round(batteryPct * 100.0F);
95 | boolean isOnCharge = (status == BatteryManager.BATTERY_STATUS_CHARGING ||
96 | status == BatteryManager.BATTERY_STATUS_FULL);
97 |
98 | if(!Build.RELEASE) {
99 | Runtime.log(context, TAG, "Battery level now " + currentLevel + " (" + batteryPct + ")", Logger.DEBUG);
100 | }
101 |
102 | // Ratchet - Are we going up from 99%?
103 | int lastLevel = prefs.getInt(Keys.PREF_KEY_CHARGE_NOTIFICATION_LAST_VALUE, -1);
104 | if(lastLevel == 99 && currentLevel == 100) {
105 | // Does the user care?
106 | boolean monitorIsEnabled = prefs.getBoolean(Keys.PREF_KEY_CHARGE_NOTIFICATION_ENABLED, false);
107 | if(monitorIsEnabled && isOnCharge) {
108 | notify(context, "Your phone is now fully charged!");
109 | Runtime.log(context, TAG, "Fully charged, notifying.", Logger.INFO);
110 | } else {
111 | Runtime.log(context, TAG, "Fully charged, but notification is not enabled. Ignoring.", Logger.INFO);
112 | }
113 | }
114 |
115 | // Dismiss on unplug
116 | if(!isPluggedIn(batteryStatus)) {
117 | try {
118 | NotificationManager mNotifyMgr = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
119 | mNotifyMgr.cancel(NOTIFICATION_ID);
120 | Runtime.log(context, TAG, "Dismissed on unplug", Logger.INFO);
121 | } catch (Exception e) {
122 | Runtime.log(context, TAG, "Exception dismissing notification, was it not present?", Logger.INFO);
123 | Runtime.logStackTrace(context, e);
124 | }
125 | }
126 |
127 | // Remember last value
128 | SharedPreferences.Editor ed = prefs.edit();
129 | ed.putInt(Keys.PREF_KEY_CHARGE_NOTIFICATION_LAST_VALUE, currentLevel);
130 | ed.commit();
131 | }
132 |
133 | private static void notify(Context context, String content) {
134 | NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
135 | if(Platform.isLollipopOrAbove()) {
136 | builder.setSmallIcon(R.drawable.ic_launcher_notif);
137 | } else {
138 | builder.setSmallIcon(R.drawable.ic_launcher);
139 | }
140 | builder.setContentTitle("Dashboard");
141 | builder.setContentText(content);
142 | builder.setColor(context.getResources().getColor(R.color.main_colour));
143 |
144 | NotificationManager mNotifyMgr = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
145 | mNotifyMgr.notify(NOTIFICATION_ID, builder.build());
146 | }
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/background/PSLCallback.java:
--------------------------------------------------------------------------------
1 | package background;
2 |
3 | /**
4 | * Simpler PSL interface for my brain
5 | */
6 | public abstract class PSLCallback {
7 |
8 | public abstract void onPercentKnown(int perc);
9 |
10 | }
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/background/PebbleReceiver.java:
--------------------------------------------------------------------------------
1 | package background;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.IntentFilter;
7 | import android.util.Log;
8 |
9 | import com.getpebble.android.kit.PebbleKit;
10 |
11 | import cl_toolkit.Logger;
12 | import config.Build;
13 | import config.Runtime;
14 | import util.PebbleUtils;
15 |
16 | import static com.getpebble.android.kit.Constants.MSG_DATA;
17 | import static com.getpebble.android.kit.Constants.TRANSACTION_ID;
18 |
19 | public class PebbleReceiver extends BroadcastReceiver {
20 | //Configuration
21 | public final static String TAG = PebbleReceiver.class.getName();
22 |
23 | private static PebbleReceiver receiver;
24 |
25 | @Override
26 | public void onReceive(Context context, Intent intent) {
27 | try {
28 | if(PebbleUtils.isThisApp(intent, Build.WATCH_APP_UUID)) {
29 | Runtime.startNewSession(context);
30 | Runtime.log(context, TAG, "Dashboard received Intent with matching UUID.", Logger.INFO);
31 |
32 | //Ack
33 | int transactionId = intent.getIntExtra(TRANSACTION_ID, -1);
34 | PebbleKit.sendAckToPebble(context, transactionId);
35 |
36 | //Get dictionary
37 | String jsonData = intent.getStringExtra(MSG_DATA);
38 | if (jsonData != null && !jsonData.isEmpty()) {
39 | //Launch service w/dictionary JSON
40 | launchHandlerService(context, jsonData);
41 |
42 | } else {
43 | Runtime.log(context, TAG, "jsonData was null or empty!", Logger.ERROR);
44 | }
45 | }
46 | } catch(Exception e) {
47 | Runtime.log(context, TAG, "Receiver threw exception: " + e.getLocalizedMessage(), Logger.ERROR);
48 | Runtime.logStackTrace(context, e);
49 | }
50 | }
51 |
52 | public static void launchHandlerService(Context context, String jsonData) {
53 | Intent service = new Intent(context, HandlerService.class);
54 | service.putExtra("json", jsonData);
55 |
56 | // Background apps are cached inactive after API 28 (Oreo) :(
57 | final int sdkInt = android.os.Build.VERSION.SDK_INT;
58 | if (sdkInt >= android.os.Build.VERSION_CODES.O) {
59 | Runtime.log(context, TAG, "startForegroundService on version " + sdkInt, Logger.INFO);
60 | context.startForegroundService(service);
61 | } else {
62 | Runtime.log(context, TAG, "startService on version " + sdkInt, Logger.INFO);
63 | context.startService(service);
64 | }
65 | }
66 |
67 | public static void registerReceiver(Context context) {
68 | unregisterReceiver(context);
69 | receiver = new PebbleReceiver();
70 |
71 | context.getApplicationContext().registerReceiver(receiver, new IntentFilter("com.getpebble.action.app.RECEIVE"));
72 |
73 | Runtime.log(context, TAG, "Registered new Pebble receiver", Logger.INFO);
74 | }
75 |
76 | public static void unregisterReceiver(Context context) {
77 | if(receiver == null) {
78 | Runtime.log(context, TAG, "Failed to unregister Pebble receiver - it was null", Logger.INFO);
79 | return;
80 | }
81 |
82 | try {
83 | context.getApplicationContext().unregisterReceiver(receiver);
84 | if(!Build.RELEASE) {
85 | Runtime.log(context, TAG, "Unregistered Pebble receiver", Logger.INFO);
86 | }
87 | } catch(Exception e) {
88 | Runtime.log(context, TAG, "Exception unregistering receiver - it may have already been unregistered", Logger.ERROR);
89 | Runtime.logStackTrace(context, e);
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/background/SignalListener.java:
--------------------------------------------------------------------------------
1 | package background;
2 |
3 | import android.content.Context;
4 | import android.telephony.PhoneStateListener;
5 | import android.telephony.SignalStrength;
6 |
7 | import cl_toolkit.Logger;
8 | import config.Runtime;
9 |
10 | // Get the phone GSM signal strength, approximately
11 | public class SignalListener extends PhoneStateListener {
12 |
13 | private static final String TAG = SignalListener.class.getName();
14 |
15 | private static final int
16 | UNKNOWN_CODE = 99,
17 | MAX_SIGNAL_DBM_VALUE = 31;
18 |
19 | private PSLCallback callback;
20 | private Context context;
21 |
22 | public SignalListener(Context context, PSLCallback callback) {
23 | this.callback = callback;
24 | this.context = context;
25 | }
26 |
27 | private int calculateSignalStrengthInPercent(int signalStrength) {
28 | Runtime.log(context, TAG, "Raw GSM strength: " + signalStrength + "/31", Logger.INFO);
29 | return (int) ((float) signalStrength / MAX_SIGNAL_DBM_VALUE * 100);
30 | }
31 |
32 | @Override
33 | public void onSignalStrengthsChanged(SignalStrength signalStrength) {
34 | super.onSignalStrengthsChanged(signalStrength);
35 |
36 | int perc;
37 | if(signalStrength != null && signalStrength.getGsmSignalStrength() != UNKNOWN_CODE) {
38 | perc = calculateSignalStrengthInPercent(signalStrength.getGsmSignalStrength());
39 | Runtime.log(context, TAG, "Phone signal strength percent = " + perc, Logger.INFO);
40 | } else {
41 | Runtime.log(context, TAG, "Unable to get phone signal strength!", Logger.INFO);
42 | perc = 0;
43 | }
44 |
45 | callback.onPercentKnown(perc);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/cl_toolkit/Contact.java:
--------------------------------------------------------------------------------
1 | package cl_toolkit;
2 |
3 | import android.content.ContentResolver;
4 | import android.content.Context;
5 | import android.database.Cursor;
6 | import android.net.Uri;
7 | import android.provider.ContactsContract.PhoneLookup;
8 |
9 | /**
10 | * Common Android Contacts helpers
11 | * @author Chris Lewis
12 | */
13 | public class Contact {
14 |
15 | /**
16 | * Get the last Contact who sent the user an SMS
17 | * @param context Context object
18 | * @return Name of last Contact or if unknown, the phone number
19 | */
20 | public static String getLastSMSName(Context context, int maxLength) {
21 | try {
22 | Cursor cursor = context.getContentResolver().query(Uri.parse("content://sms/inbox"), null, null, null, null);
23 | cursor.moveToFirst();
24 |
25 | String sender = "";
26 | for(int idx=0;idx maxLength) {
102 | return contactName.substring(0, maxLength);
103 | } else {
104 | return contactName;
105 | }
106 | } else {
107 | return phoneNumber;
108 | }
109 | } catch(Exception e) {
110 | e.printStackTrace();
111 | return phoneNumber != null ? phoneNumber : "GCNOTFOUND";
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/cl_toolkit/FileMap.java:
--------------------------------------------------------------------------------
1 | package cl_toolkit;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.File;
5 | import java.io.FileReader;
6 | import java.io.FileWriter;
7 | import java.util.HashMap;
8 |
9 | /**
10 | * Key:Value database in a file, safe and easy to use. Nothing should be uncommitted.
11 | * @author Chris Lewis
12 | */
13 | public class FileMap {
14 |
15 | private final String DELIMITER = ":";
16 |
17 | private File file;
18 |
19 | /**
20 | * Default constructor
21 | */
22 | public FileMap(String path) {
23 | try {
24 | file = new File(path);
25 |
26 | //Make a new one
27 | if(!file.exists()) {
28 | FileWriter fw = new FileWriter(file);
29 | fw.flush();
30 | fw.close();
31 | }
32 | } catch(Exception e) {
33 | System.err.println("Error opening file " + path);
34 | e.printStackTrace();
35 | }
36 | }
37 |
38 | /**
39 | * Put a value in the db
40 | * @param key Key String
41 | * @param value Value String
42 | * @return true if successful, else false
43 | */
44 | public boolean put(String key, String value) {
45 | try {
46 | //Load into hashmap
47 | HashMap map = new HashMap();
48 | BufferedReader br = new BufferedReader(new FileReader(file));
49 | String next = br.readLine();
50 | while(next != null) {
51 | String currentKey = next.substring(0, next.indexOf(DELIMITER));
52 | String currentValue = next.substring(next.indexOf(DELIMITER) + 1);
53 |
54 | //Add existing to map
55 | map.put(currentKey, currentValue);
56 |
57 | //Finally
58 | next = br.readLine();
59 | }
60 |
61 | //Add new
62 | map.put(key, value);
63 |
64 | //Save all
65 | br.close();
66 |
67 | FileWriter fw = new FileWriter(file);
68 | for(String k : map.keySet()) {
69 | fw.write(k + DELIMITER + map.get(k));
70 | fw.write("\n");
71 | }
72 |
73 | //Finally
74 | fw.flush();
75 | fw.close();
76 | return true;
77 | } catch(Exception e) {
78 | System.err.println("Error putting " + file.getAbsolutePath());
79 | e.printStackTrace();
80 | return false;
81 | }
82 | }
83 |
84 | /**
85 | * Get a value
86 | * @param key Key String
87 | * @return Value if found, else null
88 | */
89 | public String get(String key) {
90 | try {
91 | //Open file
92 | BufferedReader br = new BufferedReader(new FileReader(file));
93 |
94 | //Find the key
95 | String next = br.readLine();
96 | while(next != null) {
97 | //If it is found
98 | if(next.contains(key)) {
99 | br.close();
100 | return next.substring(next.indexOf(key + DELIMITER) + (key.length() + 1));
101 | }
102 |
103 | //Get next
104 | next = br.readLine();
105 | }
106 |
107 | //Failed
108 | // System.err.println("Could not find " + key + " in file " + file.getAbsolutePath());
109 | br.close();
110 | return null;
111 | } catch(Exception e) {
112 | System.err.println("Error getting " + key + " from " + file.getAbsolutePath());
113 | e.printStackTrace();
114 | return null;
115 | }
116 | }
117 |
118 | /**
119 | * Get a HashMap of all contained keys and values
120 | * @return HashMap of all contained keys and values
121 | */
122 | public HashMap getHashMap() {
123 | HashMap result = new HashMap();
124 |
125 | try {
126 | //Open file
127 | BufferedReader br = new BufferedReader(new FileReader(file));
128 |
129 | //Find the key
130 | String next = br.readLine();
131 | while(next != null) {
132 | //Split and add
133 | result.put(next.substring(0, next.indexOf(DELIMITER)), next.substring(next.indexOf(DELIMITER) + 1));
134 |
135 | //Get next
136 | next = br.readLine();
137 | }
138 |
139 | br.close();
140 | } catch(Exception e) {
141 | System.err.println("Error getting hashmap from " + file.getAbsolutePath());
142 | e.printStackTrace();
143 | return null;
144 | }
145 |
146 | //Finally
147 | return result;
148 | }
149 |
150 | }
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/cl_toolkit/Logger.java:
--------------------------------------------------------------------------------
1 | package cl_toolkit;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.util.Log;
5 |
6 | import java.io.File;
7 | import java.io.FileOutputStream;
8 | import java.io.FileWriter;
9 | import java.io.PrintWriter;
10 | import java.sql.Date;
11 | import java.text.SimpleDateFormat;
12 |
13 | /**
14 | * File system synchronised logger
15 | * @author Chris Lewis
16 | */
17 | public class Logger {
18 |
19 | private static final String TAG = Logger.class.getName();
20 |
21 | /**
22 | * Levels available
23 | */
24 | public static final String
25 | INFO = "[I]",
26 | ERROR = "[E]",
27 | DEBUG = "[D]";
28 |
29 | private File file;
30 |
31 | /**
32 | * Create a new log
33 | * @param path Path to output file location
34 | * @param maxSizeBytes Max size in bytes
35 | */
36 | public Logger(String path, int maxSizeBytes) {
37 | file = new File(path);
38 |
39 | //Delete when too large
40 | if(file.exists()) {
41 | long length = file.length();
42 | if(length > maxSizeBytes) {
43 | file.delete();
44 | file = new File(path);
45 | log(TAG, "Fresh new log file! (Old one was " + length + "B", Logger.INFO);
46 | }
47 | }
48 | }
49 |
50 | /**
51 | * Start a new session in the log file
52 | */
53 | public void startNewSession() {
54 | try {
55 | FileWriter fw = new FileWriter(file, true);
56 |
57 | fw.write("\n" + "===== New AppMessage session: " + getTimestamp() + " =====\n");
58 | fw.write("Size is: " + file.length() + "B " + "\n");
59 |
60 | fw.close();
61 | } catch(Exception e) {
62 | System.err.println("Error starting new log session!");
63 | e.printStackTrace();
64 | }
65 | }
66 |
67 | /**
68 | * Add a log event to the log file
69 | * @param TAG TAG of the logging class
70 | * @param message Message to log
71 | * @param levelConstant Constant value to show the level
72 | */
73 | public void log(String TAG, String message, String levelConstant) {
74 | try {
75 | FileWriter fw = new FileWriter(file, true);
76 |
77 | String complete = "[" + getTimestamp() + "] " + levelConstant + " [" + TAG + "] " + message;
78 |
79 | //Log to file
80 | fw.write(complete + "\n");
81 |
82 | //Log to ADB
83 | if(levelConstant.equals(INFO)) {
84 | Log.i(TAG, complete);
85 | } else if(levelConstant.equals(DEBUG)) {
86 | Log.d(TAG, complete);
87 | } else if(levelConstant.equals(ERROR)) {
88 | Log.e(TAG, complete);
89 | }
90 |
91 | //Finally
92 | fw.flush();
93 | fw.close();
94 | } catch(Exception e) {
95 | System.err.println("Error writing to log file!");
96 | e.printStackTrace();
97 | }
98 | }
99 |
100 | /**
101 | * Log a stack trace
102 | * @param e The Exception to print
103 | */
104 | public void logStackTrace(Exception e) {
105 | PrintWriter pw;
106 | try {
107 | pw = new PrintWriter(new FileOutputStream(file, true));
108 | e.printStackTrace(pw);
109 | pw.flush();
110 | pw.close();
111 | } catch (Exception e1) {
112 | e1.printStackTrace();
113 | }
114 | }
115 |
116 | /**
117 | * Get a timestamp string
118 | * @return time as a String
119 | */
120 | @SuppressLint("SimpleDateFormat")
121 | private String getTimestamp() {
122 | Date date = new Date(System.currentTimeMillis());
123 | SimpleDateFormat sdf = new SimpleDateFormat("dd/MM HH:mm:ss.SSS");
124 | return sdf.format(date);
125 | }
126 |
127 | }
128 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/cl_toolkit/Meta.java:
--------------------------------------------------------------------------------
1 | package cl_toolkit;
2 |
3 | public class Meta {
4 |
5 | // Version of CL Toolkit
6 | public static final String VERSION = "1.10.0";
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/cl_toolkit/Platform.java:
--------------------------------------------------------------------------------
1 | package cl_toolkit;
2 |
3 | public class Platform {
4 |
5 | public static boolean isLollipopOrAbove() {
6 | return android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP;
7 | }
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/cl_toolkit/Radios.java:
--------------------------------------------------------------------------------
1 | package cl_toolkit;
2 |
3 | import android.Manifest;
4 | import android.bluetooth.BluetoothAdapter;
5 | import android.content.Context;
6 | import android.content.pm.PackageManager;
7 | import android.net.ConnectivityManager;
8 | import android.net.NetworkInfo;
9 | import android.net.wifi.WifiConfiguration;
10 | import android.net.wifi.WifiManager;
11 | import android.os.Handler;
12 | import androidx.annotation.RequiresApi;
13 | import androidx.core.content.ContextCompat;
14 | import android.util.Log;
15 |
16 | import java.lang.reflect.Method;
17 |
18 | /**
19 | * Methods for working with device radios
20 | *
21 | * An exercise in relying on maaany reflected APIs that could vanish at the drop of a hat
22 | * > Don't do this.
23 | *
24 | * @author Chris Lewis
25 | */
26 | public class Radios {
27 |
28 | public static final int
29 | WIFI_AP_STATE_UNKNOWN = -1,
30 | WIFI_AP_STATE_DISABLING = 0,
31 | WIFI_AP_STATE_DISABLED = 1,
32 | WIFI_AP_STATE_ENABLING = 2,
33 | WIFI_AP_STATE_ENABLED = 3,
34 | WIFI_AP_STATE_FAILED = 4;
35 |
36 | private static final String TAG = Radios.class.getName();
37 |
38 | /**
39 | * Get the current WiFi state
40 | * @return true if WiFi is enabled
41 | */
42 | public static boolean getWiFiEnabled(Context context) {
43 | return ((WifiManager)context.getSystemService(Context.WIFI_SERVICE)).isWifiEnabled();
44 | }
45 |
46 | /**
47 | * Set the WiFi state
48 | * @param newState Desired new state
49 | */
50 | public static boolean setWiFiState(Context context, boolean newState) {
51 | return ((WifiManager)context.getSystemService(Context.WIFI_SERVICE)).setWifiEnabled(newState);
52 | }
53 |
54 | /**
55 | * Get the current data network state
56 | * @return true if the network radio is connected or connecting
57 | */
58 | public static boolean getMobileDataEnabled(Context context) {
59 | NetworkInfo nInfo = ((ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE))
60 | .getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
61 | return nInfo.getState() == NetworkInfo.State.CONNECTED
62 | || nInfo.getState() == NetworkInfo.State.CONNECTING;
63 | }
64 |
65 | /**
66 | * Set new Mobile Data state
67 | *
68 | * Adapted from source:
69 | * http://stackoverflow.com/questions/12535101/how-can-i-turn-off-3g-data-programmatically-on-android
70 | *
71 | * @param newState The new state
72 | */
73 | public static boolean setMobileDataEnabled(Context context, boolean newState) {
74 | try {
75 | ConnectivityManager dataManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
76 | Method dataMtd = ConnectivityManager.class.getDeclaredMethod("setMobileDataEnabled", boolean.class);
77 |
78 | dataMtd.setAccessible(true);
79 | dataMtd.invoke(dataManager, newState);
80 | return true;
81 | } catch (Exception e) {
82 | e.printStackTrace();
83 | return false;
84 | }
85 | }
86 |
87 | public static boolean setMobileDataEnabledL(Context context, boolean newState) {
88 | try {
89 | // root required ???
90 | return true;
91 | } catch(Exception e) {
92 | e.printStackTrace();
93 | return false;
94 | }
95 | }
96 |
97 | /**
98 | * Get current BT state
99 | * @return true if Bluetooth is enabled
100 | */
101 | public static boolean getBluetoothState() {
102 | return BluetoothAdapter.getDefaultAdapter().isEnabled();
103 | }
104 |
105 | /**
106 | * Set the current BT state
107 | * @param newState The desired state
108 | */
109 | public static boolean setBluetoothState(boolean newState) {
110 | BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
111 |
112 | if(newState) {
113 | return adapter.enable();
114 | } else {
115 | return adapter.disable();
116 | }
117 | }
118 |
119 | /**
120 | * Set the WiFi Access Point mode state
121 | */
122 | public static boolean setWifiApEnabled(Context context, boolean newState) {
123 | try {
124 | WifiManager manager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
125 |
126 |
127 | if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.O) {
128 | if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)
129 | {
130 | return setHotspot(manager, newState);
131 | } else {
132 | return false;
133 | }
134 | } else {
135 | if(newState) {
136 | //Disable WiFi first
137 | manager.setWifiEnabled(false);
138 | }
139 |
140 | Method method = manager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class);
141 | return (Boolean) method.invoke(manager, getWifiApConfiguration(context), newState);
142 | }
143 | } catch (Exception e) {
144 | Log.e(TAG, "Error setting Wifi AP state");
145 | e.printStackTrace();
146 | return false;
147 | }
148 | }
149 |
150 | /**
151 | * Get the WiFi AP state
152 | */
153 | private static int getWifiApState(Context context) {
154 | try {
155 | WifiManager manager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
156 | Method method = manager.getClass().getMethod("getWifiApState");
157 |
158 | int stateNow = (Integer)method.invoke(manager);
159 | if(stateNow > 10) {
160 | stateNow -= 10; // Changes in Android 4.0 for some strange reason
161 | }
162 |
163 | return stateNow;
164 | } catch (Exception e) {
165 | Log.e(TAG, "Error getting Wifi AP state");
166 | e.printStackTrace();
167 | return -1;
168 | }
169 | }
170 |
171 | /**
172 | * Simply get ON or OFF state
173 | * If in doubt, use getWifiApState and check int value
174 | */
175 | public static boolean getWifiApEnabled(Context context) {
176 | int state = getWifiApState(context);
177 | if(state == WIFI_AP_STATE_ENABLED || state == WIFI_AP_STATE_ENABLING) {
178 | return true;
179 | } else if(state == WIFI_AP_STATE_DISABLED || state == WIFI_AP_STATE_DISABLING) {
180 | return false;
181 | } else {
182 | Log.e(TAG, "Wifi AP error state: " + state);
183 | return false;
184 | }
185 | }
186 |
187 | /**
188 | * Get the Wifi AP Configuration
189 | */
190 | private static WifiConfiguration getWifiApConfiguration(Context context) {
191 | try {
192 | WifiManager manager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
193 | Method method = manager.getClass().getMethod("getWifiApConfiguration");
194 | return (WifiConfiguration)method.invoke(manager);
195 | } catch (Exception e) {
196 | Log.e(TAG, "Error getting Wifi AP Configuration");
197 | e.printStackTrace();
198 | return null;
199 | }
200 | }
201 |
202 | @RequiresApi(android.os.Build.VERSION_CODES.O)
203 | private static boolean setHotspot(WifiManager wifiManager, boolean state) {
204 | if (state) {
205 | wifiManager.startLocalOnlyHotspot(new WifiManager.LocalOnlyHotspotCallback() {
206 | @Override
207 | public void onStarted(WifiManager.LocalOnlyHotspotReservation reservation) {
208 | super.onStarted(reservation);
209 | Log.v(TAG, "WiFi AP Started");
210 | }
211 |
212 | @Override
213 | public void onStopped() {
214 | super.onStopped();
215 | Log.v(TAG, "WiFi AP Stopped");
216 | }
217 |
218 | @Override
219 | public void onFailed(int reason) {
220 | super.onFailed(reason);
221 | Log.v(TAG, "WiFi AP failed to start");
222 | }
223 | }, new Handler());
224 | } else {
225 | try {
226 | Method method = wifiManager.getClass().getDeclaredMethod("cancelLocalOnlyHotspotRequest");
227 | method.invoke(wifiManager);
228 | } catch (Exception e) {
229 | Log.v(TAG, "WiFi AP failed to stop");
230 | }
231 | }
232 | return true;
233 | }
234 | }
235 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/cl_toolkit/Root.java:
--------------------------------------------------------------------------------
1 | package cl_toolkit;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 |
6 | import java.io.DataOutputStream;
7 | import java.lang.reflect.Field;
8 |
9 | public class Root {
10 |
11 | public static final String TAG = Root.class.getName();
12 |
13 | public static final String
14 | CMD_DATA_ENABLE = "settings put global mobile_data 1",
15 | CMD_DATA_DISABLE = "settings put global mobile_data 0";
16 |
17 | /**
18 | * Big thanks:
19 | * http://stackoverflow.com/a/27011670
20 | */
21 | public static boolean rootSetMobileDataEnabled(Context context, boolean newState) {
22 | try {
23 | Process process = java.lang.Runtime.getRuntime().exec("su");
24 | DataOutputStream out = new DataOutputStream(process.getOutputStream());
25 |
26 | // The magic recipe!
27 | String cmd = "service call phone ";
28 | int rootInt = getTransactionCode("com.android.internal.telephony.ITelephony", "setDataEnabled");
29 | cmd += rootInt;
30 | if(newState) {
31 | cmd += " i32 1";
32 | } else {
33 | cmd += " i32 0";
34 | }
35 |
36 | Log.d(TAG, "Root int: " + rootInt);
37 |
38 | out.writeBytes(cmd + "\n");
39 | out.writeBytes("exit\n");
40 | out.flush();
41 | int retVal = -99; // Remember this is uninit value!
42 | synchronized (process) {
43 | retVal = process.waitFor();
44 | }
45 |
46 | Log.d(TAG, "Root mobileDataEnabled exited code " + retVal);
47 |
48 | if(retVal == 1) {
49 | // User must grant root permissions?
50 | return false;
51 | }
52 |
53 | return true;
54 | } catch(Exception e) {
55 | e.printStackTrace();
56 | return false;
57 | }
58 | }
59 |
60 | /**
61 | * Thanks to Cygery from XDA
62 | * @param baseClassName
63 | * @param targetFieldName
64 | * @return
65 | * @throws Exception
66 | */
67 | public static int getTransactionCode(String baseClassName, String targetFieldName) throws Exception {
68 | Class classToInvestigate = Class.forName(baseClassName);
69 | Class[] innerClasses = classToInvestigate.getDeclaredClasses();
70 | for (Class c : innerClasses) {
71 | Field[] aClassFields = c.getDeclaredFields();
72 | for (Field f : aClassFields) {
73 | String fieldName = f.getName();
74 | if (fieldName != null && fieldName.equals("TRANSACTION_" + targetFieldName)) {
75 | f.setAccessible(true); // that's important
76 | return f.getInt(f);
77 | }
78 | }
79 | }
80 |
81 | // Not found
82 | throw new Exception("TRANSACTION Field not found for query { " + baseClassName + ", " + targetFieldName + " }");
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/cl_toolkit/Storage.java:
--------------------------------------------------------------------------------
1 | package cl_toolkit;
2 |
3 | import android.content.Context;
4 | import android.os.Environment;
5 | import android.util.Log;
6 |
7 | import java.io.File;
8 | import java.io.InputStream;
9 |
10 | /**
11 | * Common Android storage related helpers
12 | * @author Chris Lewis
13 | */
14 | public class Storage {
15 |
16 | private static final String TAG = Storage.class.getName();
17 |
18 | /**
19 | * Get user storage directory on all devices
20 | * @return String to the directory
21 | */
22 | public static String getStorage() {
23 | return Environment.getExternalStorageDirectory().getAbsolutePath();
24 | }
25 |
26 | /**
27 | * Get the provite (data/data) directory for storage
28 | * @param context Context object for the current application
29 | * @return String in that directory
30 | */
31 | public static String getPrivateStorage(Context context) {
32 | return context.getFilesDir().getAbsolutePath();
33 | }
34 |
35 | /**
36 | * Get a directory in storage/Android/data/[app]/files/*
37 | * @param context Context object for the current application
38 | * @return String path to the location, or getExternalStorageDirectory if context or result is null
39 | */
40 | public static String getAppStorage(Context context) {
41 | String result = context.getExternalFilesDir(null).getAbsolutePath();
42 |
43 | if(result != null) {
44 | return result;
45 | } else {
46 | Log.e(TAG, "getAppStorage() returned null!");
47 | return Environment.getExternalStorageDirectory().getAbsolutePath();
48 | }
49 | }
50 |
51 | /**
52 | * Convenience method to create a new foler
53 | * @param pathToFolder Path to the new folder
54 | */
55 | public static void createFolder(String pathToFolder) {
56 | File test = new File(pathToFolder);
57 | if(!test.canRead() || !test.canWrite()) {
58 | test.mkdirs();
59 | }
60 | }
61 |
62 | /**
63 | * Get an input stream for a file in the assets folder
64 | * @param context Application context
65 | * @param assetName Name of the asset file
66 | * @return Stream of that file. Remember to .close()!
67 | */
68 | public static InputStream getAssetStream(Context context, String assetName) {
69 | try {
70 | return context.getAssets().open(assetName);
71 | } catch(Exception e) {
72 | e.printStackTrace();
73 | return null;
74 | }
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/cl_toolkit/UserInterface.java:
--------------------------------------------------------------------------------
1 | package cl_toolkit;
2 |
3 | import android.app.Activity;
4 | import android.app.AlertDialog;
5 | import android.content.ContentResolver;
6 | import android.content.Context;
7 | import android.content.DialogInterface;
8 | import android.provider.Settings;
9 |
10 | /**
11 | * Convenience methods for Activity GUI
12 | * @author Chris Lewis
13 | */
14 | public class UserInterface {
15 |
16 | public static final int
17 | BRIGHTNESS_MODE_MANUAL = 0,
18 | BRIGHTNESS_MODE_AUTO = 1;
19 |
20 | /**
21 | * Show a simple yes-no dialogue
22 | * @param context Activity context
23 | * @param title Title of the dialog
24 | * @param message Main message or question
25 | * @param positiveLabel Label for the positive button
26 | * @param positiveListener Listener for when the positive button is pressed
27 | * @param negativeLabel Label for the negative button
28 | * @param negativeListener Listener for when the negative button is pressed
29 | */
30 | public static void showDialog(Context context, String title, String message,
31 | String positiveLabel, DialogInterface.OnClickListener positiveListener,
32 | String negativeLabel, DialogInterface.OnClickListener negativeListener) {
33 | AlertDialog.Builder builder = new AlertDialog.Builder(context);
34 | builder.setTitle(title);
35 | builder.setMessage(message);
36 |
37 | if(negativeListener != null)
38 | builder.setNegativeButton(negativeLabel, negativeListener);
39 |
40 | if(positiveListener != null)
41 | builder.setPositiveButton(positiveLabel, positiveListener);
42 |
43 | AlertDialog dialog = builder.create();
44 | dialog.show();
45 | }
46 |
47 | public static void uiDelayed(final Activity a, final Runnable r, final long delay) {
48 | new Thread(new Runnable() {
49 |
50 | @Override
51 | public void run() {
52 | try {
53 | Thread.sleep(delay);
54 | a.runOnUiThread(r);
55 | } catch(Exception e) {
56 | e.printStackTrace();
57 | }
58 | }
59 |
60 | }).start();
61 | }
62 |
63 | /**
64 | * Set the state of Auto/Adaptive brightness
65 | * @param context
66 | * @param state True for auto, false for manual. Last state appears to be restored for manual
67 | */
68 | public static void setAutoBrightnessEnabled(Context context, boolean state) {
69 | ContentResolver resolver = context.getContentResolver();
70 | if(state) {
71 | Settings.System.putInt(resolver, Settings.System.SCREEN_BRIGHTNESS_MODE, BRIGHTNESS_MODE_AUTO);
72 | } else {
73 | Settings.System.putInt(resolver, Settings.System.SCREEN_BRIGHTNESS_MODE, BRIGHTNESS_MODE_MANUAL);
74 | }
75 | }
76 |
77 | /**
78 | * Get whether Auto Brightness is enabled by the user.
79 | * @param context
80 | * @return true if set to Auto/Adaptive. False otherwise.
81 | */
82 | public static boolean getAutoBrightnessEnabled(Context context) {
83 | ContentResolver resolver = context.getContentResolver();
84 | int mode;
85 | try {
86 | mode = Settings.System.getInt(resolver, Settings.System.SCREEN_BRIGHTNESS_MODE);
87 | return mode == BRIGHTNESS_MODE_AUTO ? true : false;
88 | } catch (Exception e) {
89 | System.err.println("Exception getting brightness! Returning false.");
90 | return false;
91 | }
92 | }
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/cl_toolkit/Web.java:
--------------------------------------------------------------------------------
1 | package cl_toolkit;
2 |
3 | import org.json.JSONException;
4 | import org.json.JSONObject;
5 |
6 | import java.io.BufferedReader;
7 | import java.io.InputStreamReader;
8 | import java.net.HttpURLConnection;
9 | import java.net.URL;
10 |
11 | /**
12 | * Web data related helpers
13 | * @author Chris Lewis
14 | */
15 | public class Web {
16 |
17 | /**
18 | * Download a JSON file and create it as an object
19 | * @param inUrl URL of the .json file
20 | * @return The file as a JSONObject
21 | */
22 | public static JSONObject downloadJSON(String inUrl) {
23 | try {
24 | URL url = new URL(inUrl);
25 | HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
26 |
27 | //Read file
28 | BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
29 | StringBuilder builder = new StringBuilder();
30 | String buffer = br.readLine();
31 | while(buffer != null) {
32 | builder.append(buffer);
33 | buffer = br.readLine();
34 | }
35 |
36 | //Parse results
37 | return new JSONObject(builder.toString());
38 | } catch(Exception e) {
39 | e.printStackTrace();
40 |
41 | JSONObject errObj = new JSONObject();
42 | try {
43 | errObj.put("error", "Error downloading JSON!");
44 | } catch (JSONException e1) {
45 | e1.printStackTrace();
46 | }
47 | return errObj;
48 | }
49 | }
50 |
51 | public static String downloadHTML(String inUrl) {
52 | try {
53 | URL url = new URL(inUrl);
54 | HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
55 |
56 | //Read file
57 | BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
58 | StringBuilder builder = new StringBuilder();
59 | String buffer = br.readLine();
60 | while(buffer != null) {
61 | builder.append(buffer);
62 |
63 | buffer = br.readLine();
64 | }
65 |
66 | //Parse results
67 | return builder.toString();
68 | } catch(Exception e) {
69 | e.printStackTrace();
70 | return "error";
71 | }
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/config/Build.java:
--------------------------------------------------------------------------------
1 | package config;
2 |
3 | import java.util.UUID;
4 |
5 | /**
6 | * Build-time data
7 | */
8 | public class Build {
9 |
10 | /** Android app version string */
11 | public static final String VERSION = "4.17";
12 |
13 | /** Pebble watchapp UUID */
14 | public static final UUID WATCH_APP_UUID = UUID.fromString("d522bc8e-65f3-4edf-9651-05e1e4567021");
15 | /** Log filename */
16 | public static final String DEBUG_LOG_NAME = "Dashboard-log.txt";
17 | /** Android store package name */
18 | public static final String PACKAGE_NAME = "com.wordpress.ninedof.dashboard";
19 | /** Max size of debug log before re-creating */
20 | public static final int DEBUG_LOG_MAX_SIZE_BYTES = 100000; // 100kB
21 | /** Most recent compatible watchapp version */
22 | public static final String WATCH_APP_COMPATIBLE_VERSION = "4.8";
23 | /** Hide some log details like the protocol in production */
24 | public static final boolean RELEASE = true;
25 | /** Number of toggles - used for safe iteration over all configuration spinners (NEVER CHANGE THIS AGAIN, may cause problems with saved config on both sides) */
26 | public static final int NUM_TOGGLES = 9;
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/config/Keys.java:
--------------------------------------------------------------------------------
1 | package config;
2 |
3 | public class Keys {
4 |
5 | // Preference keys
6 | public static final String
7 | PREF_FIRST_RUN = "first-run",
8 | PREF_CHANGELOG_PREFIX = "changelog",
9 | PREF_CONFIGURE_BASE = "conf",
10 | PREF_FIND_PHONE_RUNNING = "find-phone-running",
11 | // PREF_INVERT = "invert", // Removed in v2.0
12 | // PREF_ROOT_DISABLE = "pref_key_root_disable", // Removed when PREF_KEY_DATA_ENABLED was introduced to make data toggle explicit opt-in
13 | PREF_KEY_ADMIN = "pref_key_admin",
14 | PREF_KEY_VERSION = "pref_key_version",
15 | PREF_KEY_DATA_ENABLED = "pref_key_data_enabled",
16 | PREF_KEY_ROOT_ACCESS = "pref_key_root_access",
17 | PREF_KEY_QUICK_LAUNCH_TYPE = "pref_key_quick_launch_type",
18 | PREF_KEY_QUICK_LAUNCH_ENABLED = "pref_key_quick_launch_enabled",
19 | PREF_KEY_CHARGE_NOTIFICATION_ENABLED = "pref_key_charge_notification_enabled",
20 | PREF_KEY_CHARGE_NOTIFICATION_LAST_VALUE = "pref_key_charge_notification_last_value",
21 | PREF_KEY_FIND_PHONE_FILE = "pref_key_find_phone_file",
22 | PREF_KEY_FIND_PHONE_TITLE = "pref_key_find_phone_title",
23 | PREF_VALUE_FIND_PHONE_DEFAULT = "pref_value_find_phone_no_file";
24 |
25 |
26 | // AppMessage keys - mirror pebble/src/c/types.h
27 | public static final int
28 | AppKeyGSMName = 0,
29 | AppKeyGSMPercent = 1,
30 | AppKeyWifiName = 2,
31 | AppKeyBatteryPercent = 3,
32 | AppKeyStorageFreeGBMajor = 4,
33 | AppKeyStorageFreeGBMinor = 5,
34 | AppKeyToggleWifi = 6,
35 | AppKeyToggleData = 7,
36 | AppKeyToggleBluetooth = 8,
37 | AppKeyToggleRinger = 9,
38 | AppKeyToggleSync = 10,
39 | AppKeyToggleWifiAP = 11,
40 | AppKeyToggleFindPhone = 12,
41 | AppKeyToggleLockPhone = 13,
42 | AppKeyToggleAutoBrightness = 14,
43 | AppKeyToggleOrderString = 15,
44 | AppKeyIsLollipop = 16,
45 | AppKeyStoragePercent = 17,
46 | AppKeyQuickLaunchEnabled = 18,
47 | AppKeyQuickLaunchType = 19,
48 | AppKeyVersion = 20,
49 |
50 | ToggleTypeWifi = 0,
51 | ToggleTypeData = 1,
52 | ToggleTypeBluetooth = 2,
53 | ToggleTypeRinger = 3,
54 | ToggleTypeSync = 4,
55 | ToggleTypeWifiAP = 5,
56 | ToggleTypeFindPhone = 6,
57 | ToggleTypeLockPhone = 7,
58 | ToggleTypeAutoBrightness = 8,
59 |
60 | ToggleStateWaiting = 0,
61 | ToggleStateOff = 1,
62 | ToggleStateOn = 2,
63 | ToggleStateLoud = 3,
64 | ToggleStateVibrate = 4,
65 | ToggleStateSilent = 5,
66 | ToggleStateBrightnessAutomatic = 6,
67 | ToggleStateBrightnessManual = 7,
68 | ToggleStateLocked = 8,
69 |
70 | ErrorCodeNoRoot = 0,
71 | ErrorCodeNoDeviceAdmin = 1,
72 | ErrorCodeLockSuccess = 2,
73 | ErrorCodeDataNotEnabled = 3,
74 | ErrorCodeWrongVersion = 40, // 4 clashes with AppKeyStorageFreeGBMajor
75 |
76 | MessageTypeRequestAll = 543,
77 | MessageTypeToggle = 544;
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/config/Runtime.java:
--------------------------------------------------------------------------------
1 | package config;
2 |
3 | import android.content.Context;
4 |
5 | import cl_toolkit.Logger;
6 | import cl_toolkit.Storage;
7 |
8 | /**
9 | * Run-time data
10 | * @author Chris Lewis
11 | */
12 | public class Runtime {
13 |
14 | private static Logger getLogger(Context context) {
15 | return new Logger(Storage.getAppStorage(context) + "/" + Build.DEBUG_LOG_NAME, Build.DEBUG_LOG_MAX_SIZE_BYTES);
16 | }
17 |
18 | public static void log(Context context, String TAG, String message, String level) {
19 | getLogger(context).log(TAG, message, level);
20 | }
21 |
22 | public static void logStackTrace(Context context, Exception e) {
23 | getLogger(context).logStackTrace(e);
24 | }
25 |
26 | public static void startNewSession(Context context) {
27 | getLogger(context).startNewSession();
28 | }
29 |
30 | }
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/fragment/About.java:
--------------------------------------------------------------------------------
1 | package fragment;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.preference.CheckBoxPreference;
7 | import android.preference.Preference;
8 | import android.preference.Preference.OnPreferenceClickListener;
9 | import android.preference.PreferenceCategory;
10 | import android.preference.PreferenceFragment;
11 | import android.preference.PreferenceScreen;
12 | import android.widget.Toast;
13 |
14 | import com.stericson.RootTools.RootTools;
15 | import com.wordpress.ninedof.dashboard.R;
16 |
17 | import activity.LogViewer;
18 | import cl_toolkit.Logger;
19 | import cl_toolkit.Platform;
20 | import config.Build;
21 | import config.Keys;
22 | import config.Runtime;
23 |
24 | public class About extends PreferenceFragment {
25 |
26 | private static final String TAG = About.class.getName();
27 |
28 | @Override
29 | public void onCreate(Bundle savedInstanceState) {
30 | super.onCreate(savedInstanceState);
31 |
32 | final Context context = getActivity().getApplicationContext();
33 | addPreferencesFromResource(R.xml.preferences);
34 |
35 | // Set version string
36 | Preference about = (Preference)findPreference("pref_key_version");
37 | about.setTitle("Dashboard v" + Build.VERSION + " by Chris Lewis");
38 |
39 | //Load prefs
40 | if(Platform.isLollipopOrAbove()) {
41 | // Enable Data toggle
42 | CheckBoxPreference dataPref = (CheckBoxPreference)findPreference(Keys.PREF_KEY_DATA_ENABLED);
43 | if(dataPref != null) {
44 | dataPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
45 |
46 | @Override
47 | public boolean onPreferenceClick(Preference preference) {
48 | if (((CheckBoxPreference)preference).isChecked()) {
49 | if(!testRoot()) {
50 | Toast.makeText(context, "Enabled, but root access is not available. The Data toggle will not work.", Toast.LENGTH_LONG).show();
51 | }
52 | }
53 | return true;
54 | }
55 |
56 | });
57 | }
58 |
59 | Preference rootTestPref = findPreference(Keys.PREF_KEY_ROOT_ACCESS);
60 | if (rootTestPref != null) {
61 | rootTestPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
62 |
63 | @Override
64 | public boolean onPreferenceClick(Preference preference) {
65 | boolean success = testRoot();
66 | Toast.makeText(context, "Root access test " +
67 | (success ? "succeeded!" : "failed! Device may not be rooted, or access was not granted."), Toast.LENGTH_LONG).show();
68 | return true;
69 | }
70 |
71 | });
72 | } else {
73 | Runtime.log(context, TAG, "rootTestPref null!", Logger.ERROR);
74 | }
75 | } else {
76 | // Not required, remove root stuff
77 | PreferenceScreen advancedScreen = (PreferenceScreen)findPreference("pref_screen");
78 | PreferenceCategory advancedCategory = (PreferenceCategory)findPreference("pref_category_advanced");
79 | advancedScreen.removePreference(advancedCategory);
80 | }
81 |
82 | //Debug log
83 | Preference logPref = findPreference(Keys.PREF_KEY_VERSION);
84 | logPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
85 |
86 | @Override
87 | public boolean onPreferenceClick(Preference preference) {
88 | // Show log viewer
89 | Intent i = new Intent(getActivity(), LogViewer.class);
90 | startActivity(i);
91 | return true;
92 | }
93 |
94 | });
95 | }
96 |
97 | private boolean testRoot() {
98 | Context context = getActivity().getApplicationContext();
99 |
100 | // Test root access
101 | boolean success;
102 | try {
103 | success = RootTools.isAccessGiven();
104 | } catch(Exception e) {
105 | Runtime.log(context, TAG, "Failed to get root access", Logger.ERROR);
106 | Runtime.logStackTrace(context, e);
107 | success = false;
108 | }
109 |
110 | Runtime.log(context, TAG, "Root test result: " + success, Logger.INFO);
111 | return success;
112 | }
113 |
114 | }
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/util/PebbleUtils.java:
--------------------------------------------------------------------------------
1 | package util;
2 |
3 | import android.app.AlertDialog;
4 | import android.content.Context;
5 | import android.content.DialogInterface;
6 | import android.content.Intent;
7 | import android.net.Uri;
8 | import android.widget.Toast;
9 |
10 | import com.getpebble.android.kit.util.PebbleDictionary;
11 |
12 | import java.io.File;
13 | import java.io.FileOutputStream;
14 | import java.io.IOException;
15 | import java.io.InputStream;
16 | import java.io.OutputStream;
17 | import java.util.UUID;
18 |
19 | public class PebbleUtils {
20 |
21 | /**
22 | * Appstore install for 2.0 use (deep link reliability may vary between offical Pebble app releases...)
23 | * @param appStoreUID UID from dev-portal.getpebble.com
24 | */
25 | public static void appStoreInstall(Context context, String appStoreUID) {
26 | Intent intent = new Intent(Intent.ACTION_VIEW);
27 | intent.setData(Uri.parse("pebble://appstore/" + appStoreUID));
28 | intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
29 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
30 | context.startActivity(intent);
31 | }
32 |
33 | /**
34 | * Alternative sideloading method
35 | * Source: http://forums.getpebble.com/discussion/comment/103733/#Comment_103733
36 | * @param assetFilename File name of the asset to sideload
37 | */
38 | public static void sideloadInstall(Context ctx, String assetFilename) {
39 | try {
40 | Intent intent = new Intent(Intent.ACTION_VIEW);
41 | File file = new File(ctx.getExternalFilesDir(null), assetFilename);
42 | InputStream is = ctx.getResources().getAssets().open(assetFilename);
43 | OutputStream os = new FileOutputStream(file);
44 | byte[] pbw = new byte[is.available()];
45 | is.read(pbw);
46 | os.write(pbw);
47 | is.close();
48 | os.close();
49 | intent.setDataAndType(Uri.fromFile(file), "application/pbw");
50 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
51 | ctx.startActivity(intent);
52 | } catch (IOException e) {
53 | Toast.makeText(ctx, "App install failed: " + e.getLocalizedMessage(), Toast.LENGTH_LONG).show();
54 | }
55 | }
56 |
57 | /**
58 | * Let user choose install source for 2.0 use
59 | * @param pbwFilename File name for .pbw option
60 | * @param appstoreUid UID from dev-portal.getpebble.com
61 | */
62 | public static void installEither(final Context context, final String pbwFilename, final String appstoreUid) {
63 | AlertDialog.Builder dialog = new AlertDialog.Builder(context);
64 | dialog.setTitle("Install Watchapp");
65 | dialog.setMessage("Choose your preferred method of installation.");
66 | dialog.setPositiveButton("Local .pbw", new DialogInterface.OnClickListener() {
67 |
68 | @Override
69 | public void onClick(DialogInterface dialog, int which) {
70 | sideloadInstall(context, pbwFilename);
71 | }
72 |
73 | });
74 | dialog.setNeutralButton("Pebble Appstore", new DialogInterface.OnClickListener() {
75 |
76 | @Override
77 | public void onClick(DialogInterface dialog, int which) {
78 | try {
79 | appStoreInstall(context, appstoreUid);
80 | } catch (Exception e) {
81 | Toast.makeText(context, "Pebble Appstore not found! Try 'Local.pbw' instead.", Toast.LENGTH_SHORT).show();
82 | }
83 | }
84 |
85 | });
86 | dialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
87 |
88 | @Override
89 | public void onClick(DialogInterface dialog, int which) {
90 | dialog.dismiss();
91 | }
92 |
93 | });
94 | dialog.show();
95 | }
96 |
97 | /**
98 | * See if the Pebble Dictionary has a TupletInteger
99 | * @param dict Dictionary that may contain the key
100 | * @param key The key itself to look for data.
101 | * @return true if a K-V pair is there
102 | */
103 | public static boolean hasInt(PebbleDictionary dict, int key) {
104 | return dict.getInteger(key) != null;
105 | }
106 |
107 | /**
108 | * Get a TupletInteger from the dictionary
109 | * @param dict Dictionary that contains the key
110 | * @param key The key to look for
111 | * @return the value associated with the key
112 | */
113 | public static int getInt(PebbleDictionary dict, int key) {
114 | return dict.getInteger(key).intValue();
115 | }
116 |
117 | /**
118 | * See if the Pebble Dictionary has a TupletCString
119 | * @param dict Dictionary that may contain the key
120 | * @param key The key itself to look for data.
121 | * @return true if a K-V pair is there
122 | */
123 | public static boolean hasString(PebbleDictionary dict, int key) {
124 | return dict.getString(key) != null;
125 | }
126 |
127 | /**
128 | * Check a Receiver's intent to see if it is for this app
129 | * @param intent Broadcast intent containing Pebble data
130 | * @param thisAppUUID UUID of this app
131 | * @return true if the two match
132 | */
133 | public static boolean isThisApp(Intent intent, UUID thisAppUUID) {
134 | return intent.getSerializableExtra("uuid").equals(thisAppUUID);
135 | }
136 |
137 | }
138 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/java/util/VersionCheck.java:
--------------------------------------------------------------------------------
1 | package util;
2 |
3 | import java.util.UUID;
4 |
5 | import android.content.Context;
6 | import android.util.Log;
7 |
8 | import com.getpebble.android.kit.PebbleKit;
9 | import com.getpebble.android.kit.util.PebbleDictionary;
10 |
11 | /**
12 | * Check watchapp version against a known compatible version on Android
13 | * 1. Call version_check(char *version, void(*callback)(bool)); from watchapp
14 | * 2. On reception of that dictionary, call check() below to issue response and trigger callback
15 | *
16 | * @author Chris Lewis
17 | */
18 | public class VersionCheck {
19 |
20 | private static final String TAG = VersionCheck.class.getName();
21 |
22 | public static final int
23 | KEY_VERSION_CHECK_VERSION = 58283,
24 | KEY_VERSION_CHECK_SUCCESS = 58278,
25 | KEY_VERSION_CHECK_FAILURE = 58232;
26 |
27 | /**
28 | * To be called from a PebbleDataReceiver implementation
29 | * @param context Context of the Android app
30 | * @param uuid UUID of the watchapp
31 | * @param dict Incoming dictionary
32 | * @param localVersion Compatible version with this Android app version
33 | */
34 | public static void check(Context context, UUID uuid, PebbleDictionary dict, String localVersion) {
35 | if(dict.getString(KEY_VERSION_CHECK_VERSION) != null) {
36 | String remoteVersion = dict.getString(KEY_VERSION_CHECK_VERSION).toString();
37 |
38 | //Let the watch know
39 | PebbleDictionary response = new PebbleDictionary();
40 |
41 | if(localVersion.equals(remoteVersion)) {
42 | response.addInt32(KEY_VERSION_CHECK_SUCCESS, 0);
43 | Log.d(TAG, "Version check passed!");
44 | } else {
45 | response.addInt32(KEY_VERSION_CHECK_FAILURE, 0);
46 | Log.d(TAG, "Version check failed!");
47 | }
48 |
49 | PebbleKit.sendDataToPebble(context, uuid, response);
50 | }
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/anim/slide_in_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
7 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-hdpi/ic_launcher_notif.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-hdpi/ic_launcher_notif.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-mdpi/ic_launcher_notif.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-mdpi/ic_launcher_notif.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/ap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/ap.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/brightness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/brightness.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/bt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/bt.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/button.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/button.9.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/data.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/data.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/empty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/empty.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/framed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/framed.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/ic_launcher_notif.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/ic_launcher_notif.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/ic_launcher_notif_large.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/ic_launcher_notif_large.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/lock.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/loud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/loud.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/mailsrc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/mailsrc.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/paypal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/paypal.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/phone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/phone.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/priority.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/priority.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/questionmark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/questionmark.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/rate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/rate.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/silent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/silent.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/sync.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/sync.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/tweet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/tweet.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/vibrate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/vibrate.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/wifi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/wifi.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/drawable-xhdpi/wordpress.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/DashboardRedux/src/main/res/drawable-xhdpi/wordpress.png
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/layout/activity_log_viewer.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/layout/settings_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/layout/toggle_spinner_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
19 |
20 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/menu/menu_landing.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/menu/menu_log_viewer.xml:
--------------------------------------------------------------------------------
1 |
18 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 | 64dp
9 |
10 |
11 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/values/colours.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #AA0000
4 | #880000
5 | #990000
6 |
7 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Dashboard
5 | Settings
6 | LandingL
7 | Hello world!
8 |
9 |
10 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
20 |
21 |
22 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/xml/actionbar_tab_indicator.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 |
13 |
14 |
15 |
16 |
17 |
20 |
23 |
24 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/xml/admin.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/android/DashboardRedux/src/main/res/xml/preferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
8 |
9 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
31 |
34 |
35 |
40 |
43 |
44 |
45 |
50 |
53 |
54 |
59 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/android/PebbleHomescreen.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/android/android.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
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 | google()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:4.0.2'
10 |
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | jcenter()
19 | google()
20 | }
21 | }
22 |
23 | task clean(type: Delete) {
24 | delete rootProject.buildDir
25 | }
26 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | android.enableJetifier=true
2 | android.useAndroidX=true
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Oct 10 17:00:42 BST 2020
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-6.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
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 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
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 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
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 Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':DashboardRedux'
2 |
--------------------------------------------------------------------------------
/assets/listing/app_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/app_logo.png
--------------------------------------------------------------------------------
/assets/listing/blogbanner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/blogbanner.png
--------------------------------------------------------------------------------
/assets/listing/icon-large.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/icon-large.png
--------------------------------------------------------------------------------
/assets/listing/icon-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/icon-small.png
--------------------------------------------------------------------------------
/assets/listing/icons-original-size/ap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/icons-original-size/ap.png
--------------------------------------------------------------------------------
/assets/listing/icons-original-size/brightness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/icons-original-size/brightness.png
--------------------------------------------------------------------------------
/assets/listing/icons-original-size/bt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/icons-original-size/bt.png
--------------------------------------------------------------------------------
/assets/listing/icons-original-size/data.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/icons-original-size/data.png
--------------------------------------------------------------------------------
/assets/listing/icons-original-size/lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/icons-original-size/lock.png
--------------------------------------------------------------------------------
/assets/listing/icons-original-size/loud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/icons-original-size/loud.png
--------------------------------------------------------------------------------
/assets/listing/icons-original-size/phone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/icons-original-size/phone.png
--------------------------------------------------------------------------------
/assets/listing/icons-original-size/priority.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/icons-original-size/priority.png
--------------------------------------------------------------------------------
/assets/listing/icons-original-size/silent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/icons-original-size/silent.png
--------------------------------------------------------------------------------
/assets/listing/icons-original-size/sync.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/icons-original-size/sync.png
--------------------------------------------------------------------------------
/assets/listing/icons-original-size/vibrate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/icons-original-size/vibrate.png
--------------------------------------------------------------------------------
/assets/listing/icons-original-size/wifi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/icons-original-size/wifi.png
--------------------------------------------------------------------------------
/assets/listing/marketing-aplite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/marketing-aplite.png
--------------------------------------------------------------------------------
/assets/listing/marketing-basalt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/marketing-basalt.png
--------------------------------------------------------------------------------
/assets/listing/marketing-chalk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/marketing-chalk.png
--------------------------------------------------------------------------------
/assets/listing/marketing-gplay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/marketing-gplay.png
--------------------------------------------------------------------------------
/assets/listing/marketing-src-aplite.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/marketing-src-aplite.xcf
--------------------------------------------------------------------------------
/assets/listing/marketing-src-basalt.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/marketing-src-basalt.xcf
--------------------------------------------------------------------------------
/assets/listing/marketing-src-chalk.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/marketing-src-chalk.xcf
--------------------------------------------------------------------------------
/assets/listing/marketing-src-gplay.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/listing/marketing-src-gplay.xcf
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-1.13.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-1.13.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-1.14.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-1.14.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-1.15.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-1.15.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-1.16.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-1.16.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-1.17.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-1.17.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-1.19.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-1.19.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-1.20.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-1.20.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-3.0.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-3.0.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-3.1.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-3.1.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-3.2.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-3.2.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-4.0.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-4.0.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-4.1.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-4.1.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-4.10.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-4.10.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-4.11.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-4.11.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-4.12.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-4.12.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-4.14.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-4.14.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-4.15.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-4.15.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-4.16.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-4.16.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-4.17.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-4.17.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-4.2.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-4.2.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-4.3.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-4.3.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-4.4.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-4.4.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-4.5.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-4.5.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-4.6.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-4.6.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-4.7.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-4.7.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-4.8.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-4.8.apk
--------------------------------------------------------------------------------
/assets/releases/Dashboard-release-4.9.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/Dashboard-release-4.9.apk
--------------------------------------------------------------------------------
/assets/releases/dashboard-4.0.pbw:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/dashboard-4.0.pbw
--------------------------------------------------------------------------------
/assets/releases/dashboard-4.1.pbw:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/dashboard-4.1.pbw
--------------------------------------------------------------------------------
/assets/releases/dashboard-4.2.pbw:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/dashboard-4.2.pbw
--------------------------------------------------------------------------------
/assets/releases/dashboard-4.3.pbw:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/dashboard-4.3.pbw
--------------------------------------------------------------------------------
/assets/releases/dashboard-4.5.pbw:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/dashboard-4.5.pbw
--------------------------------------------------------------------------------
/assets/releases/dashboard-4.6.pbw:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/dashboard-4.6.pbw
--------------------------------------------------------------------------------
/assets/releases/dashboard-4.8.pbw:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/releases/dashboard-4.8.pbw
--------------------------------------------------------------------------------
/assets/screenshots/Screenshot_20160504-164142.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/screenshots/Screenshot_20160504-164142.png
--------------------------------------------------------------------------------
/assets/screenshots/Screenshot_20160504-164150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/screenshots/Screenshot_20160504-164150.png
--------------------------------------------------------------------------------
/assets/screenshots/aplite1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/screenshots/aplite1.png
--------------------------------------------------------------------------------
/assets/screenshots/aplite2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/screenshots/aplite2.png
--------------------------------------------------------------------------------
/assets/screenshots/aplite3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/screenshots/aplite3.png
--------------------------------------------------------------------------------
/assets/screenshots/aplite4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/screenshots/aplite4.png
--------------------------------------------------------------------------------
/assets/screenshots/basalt1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/screenshots/basalt1.png
--------------------------------------------------------------------------------
/assets/screenshots/basalt2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/screenshots/basalt2.png
--------------------------------------------------------------------------------
/assets/screenshots/basalt3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/screenshots/basalt3.png
--------------------------------------------------------------------------------
/assets/screenshots/basalt4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/screenshots/basalt4.png
--------------------------------------------------------------------------------
/assets/screenshots/chalk1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/screenshots/chalk1.png
--------------------------------------------------------------------------------
/assets/screenshots/chalk2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/screenshots/chalk2.png
--------------------------------------------------------------------------------
/assets/screenshots/chalk3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/screenshots/chalk3.png
--------------------------------------------------------------------------------
/assets/screenshots/chalk4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/assets/screenshots/chalk4.png
--------------------------------------------------------------------------------
/pebble/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Dashboard",
3 | "version": "4.8.0",
4 | "author": "Chris Lewis",
5 | "private": true,
6 | "dependencies": {},
7 | "keywords": [
8 | "pebble-app"
9 | ],
10 | "pebble": {
11 | "sdkVersion": "3",
12 | "resources": {
13 | "media": [
14 | {
15 | "type": "bitmap",
16 | "name": "LOGO",
17 | "file": "images/logo.png"
18 | },
19 | {
20 | "type": "bitmap",
21 | "name": "TICK",
22 | "file": "images/tick.png"
23 | },
24 | {
25 | "type": "bitmap",
26 | "name": "UP",
27 | "file": "images/up.png"
28 | },
29 | {
30 | "type": "bitmap",
31 | "name": "SELECT",
32 | "file": "images/select.png"
33 | },
34 | {
35 | "type": "bitmap",
36 | "name": "DOWN",
37 | "file": "images/down.png"
38 | },
39 | {
40 | "type": "bitmap",
41 | "name": "WIFI",
42 | "file": "icons/wifi.png"
43 | },
44 | {
45 | "type": "bitmap",
46 | "name": "DATA",
47 | "file": "icons/data.png"
48 | },
49 | {
50 | "type": "bitmap",
51 | "name": "BLUETOOTH",
52 | "file": "icons/bt.png"
53 | },
54 | {
55 | "type": "bitmap",
56 | "name": "RINGER_LOUD",
57 | "file": "icons/loud.png"
58 | },
59 | {
60 | "type": "bitmap",
61 | "name": "RINGER_VIBRATE",
62 | "file": "icons/vibrate.png"
63 | },
64 | {
65 | "type": "bitmap",
66 | "name": "RINGER_SILENT",
67 | "file": "icons/silent.png"
68 | },
69 | {
70 | "type": "bitmap",
71 | "name": "RINGER_PRIORITY",
72 | "file": "icons/priority.png"
73 | },
74 | {
75 | "type": "bitmap",
76 | "name": "SYNC",
77 | "file": "icons/sync.png"
78 | },
79 | {
80 | "type": "bitmap",
81 | "name": "WIFI_AP",
82 | "file": "icons/ap.png"
83 | },
84 | {
85 | "type": "bitmap",
86 | "name": "FIND_PHONE",
87 | "file": "icons/phone.png"
88 | },
89 | {
90 | "type": "bitmap",
91 | "name": "LOCK_PHONE",
92 | "file": "icons/lock.png"
93 | },
94 | {
95 | "type": "bitmap",
96 | "name": "BRIGHTNESS",
97 | "file": "icons/brightness.png"
98 | },
99 | {
100 | "menuIcon": true,
101 | "type": "bitmap",
102 | "name": "APP_ICON",
103 | "file": "images/app_icon.png"
104 | }
105 | ]
106 | },
107 | "projectType": "native",
108 | "uuid": "d522bc8e-65f3-4edf-9651-05e1e4567021",
109 | "messageKeys": {
110 | "dummy": 0
111 | },
112 | "enableMultiJS": true,
113 | "displayName": "Dashboard",
114 | "watchapp": {
115 | "onlyShownOnCommunication": false,
116 | "hiddenApp": false,
117 | "watchface": false
118 | },
119 | "targetPlatforms": [
120 | "aplite",
121 | "basalt",
122 | "chalk",
123 | "diorite"
124 | ],
125 | "capabilities": []
126 | }
127 | }
--------------------------------------------------------------------------------
/pebble/resources/icons/ap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/pebble/resources/icons/ap.png
--------------------------------------------------------------------------------
/pebble/resources/icons/brightness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/pebble/resources/icons/brightness.png
--------------------------------------------------------------------------------
/pebble/resources/icons/bt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/pebble/resources/icons/bt.png
--------------------------------------------------------------------------------
/pebble/resources/icons/data.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/pebble/resources/icons/data.png
--------------------------------------------------------------------------------
/pebble/resources/icons/lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/pebble/resources/icons/lock.png
--------------------------------------------------------------------------------
/pebble/resources/icons/loud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/pebble/resources/icons/loud.png
--------------------------------------------------------------------------------
/pebble/resources/icons/phone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/pebble/resources/icons/phone.png
--------------------------------------------------------------------------------
/pebble/resources/icons/priority.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/pebble/resources/icons/priority.png
--------------------------------------------------------------------------------
/pebble/resources/icons/silent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/pebble/resources/icons/silent.png
--------------------------------------------------------------------------------
/pebble/resources/icons/sync.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/pebble/resources/icons/sync.png
--------------------------------------------------------------------------------
/pebble/resources/icons/vibrate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/pebble/resources/icons/vibrate.png
--------------------------------------------------------------------------------
/pebble/resources/icons/wifi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/pebble/resources/icons/wifi.png
--------------------------------------------------------------------------------
/pebble/resources/images/app_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/pebble/resources/images/app_icon.png
--------------------------------------------------------------------------------
/pebble/resources/images/down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/pebble/resources/images/down.png
--------------------------------------------------------------------------------
/pebble/resources/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/pebble/resources/images/logo.png
--------------------------------------------------------------------------------
/pebble/resources/images/select.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/pebble/resources/images/select.png
--------------------------------------------------------------------------------
/pebble/resources/images/tick.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/pebble/resources/images/tick.png
--------------------------------------------------------------------------------
/pebble/resources/images/up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C-D-Lewis/dashboard/76439ff18b98bf13ec317ec0ab7913a3c546c41c/pebble/resources/images/up.png
--------------------------------------------------------------------------------
/pebble/src/c/config.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #define TEST false // Use local mock data
6 | #define DEBUG false
7 |
8 | #define VERSION "4.8"
9 |
10 | #define BUFFER_SIZE_IN 256
11 | #define BUFFER_SIZE_OUT 64
12 | #define INITIAL_DELAY 100
13 | #define NETWORK_MAX_LENGTH 32
14 | #define COMM_TIMEOUT_MS 1000
15 | #define SYNC_TIMER_INTERVAL_S 30
16 | #define TRANSITION_DURATION 200
17 | #define MAX_WAKEUPS 8
--------------------------------------------------------------------------------
/pebble/src/c/lib/new_number_window/new_number_window.c:
--------------------------------------------------------------------------------
1 | #include "new_number_window.h"
2 |
3 | static Window *s_window;
4 | static TextLayer *s_title_layer, *s_value_layer;
5 | static ActionBarLayer *s_action_bar;
6 | static StatusBarLayer *s_status_bar;
7 |
8 | static GBitmap *s_up_bitmap, *s_select_bitmap, *s_down_bitmap;
9 | static NewNumberWindowCallback *s_callback;
10 | static char s_title_buffer[32];
11 | static int s_min, s_max, s_step, s_value;
12 |
13 | static void set_value() {
14 | static char s_buff[8];
15 | snprintf(s_buff, sizeof(s_buff), "%d", s_value);
16 | text_layer_set_text(s_value_layer, s_buff);
17 | }
18 |
19 | static void select_click_handler(ClickRecognizerRef recognizer, void *context) {
20 | // Accept value
21 | window_stack_pop(true);
22 | s_callback(s_value);
23 | }
24 |
25 | static void up_click_handler(ClickRecognizerRef recognizer, void *context) {
26 | if(s_value < s_max) {
27 | s_value += s_step;
28 | }
29 | set_value();
30 | }
31 |
32 | static void down_click_handler(ClickRecognizerRef recognizer, void *context) {
33 | if(s_value > s_min) {
34 | s_value -= s_step;
35 | }
36 | set_value();
37 | }
38 |
39 | static void click_config_provider(void *context) {
40 | window_single_click_subscribe(BUTTON_ID_SELECT, select_click_handler);
41 | window_single_repeating_click_subscribe(BUTTON_ID_UP, 200, up_click_handler);
42 | window_single_repeating_click_subscribe(BUTTON_ID_DOWN, 200, down_click_handler);
43 | }
44 |
45 | static void window_load(Window *window) {
46 | Layer *window_layer = window_get_root_layer(window);
47 | GRect bounds = layer_get_bounds(window_layer);
48 |
49 | s_up_bitmap = gbitmap_create_with_resource(RESOURCE_ID_UP);
50 | s_select_bitmap = gbitmap_create_with_resource(RESOURCE_ID_SELECT);
51 | s_down_bitmap = gbitmap_create_with_resource(RESOURCE_ID_DOWN);
52 |
53 | s_action_bar = action_bar_layer_create();
54 | action_bar_layer_set_icon(s_action_bar, BUTTON_ID_UP, s_up_bitmap);
55 | action_bar_layer_set_icon(s_action_bar, BUTTON_ID_SELECT, s_select_bitmap);
56 | action_bar_layer_set_icon(s_action_bar, BUTTON_ID_DOWN, s_down_bitmap);
57 | action_bar_layer_set_click_config_provider(s_action_bar, click_config_provider);
58 | action_bar_layer_add_to_window(s_action_bar, window);
59 |
60 | const int x_margin = PBL_IF_ROUND_ELSE(0, ACTION_BAR_WIDTH);
61 | const int y_margin = PBL_IF_ROUND_ELSE(35, 25);
62 | s_title_layer = text_layer_create(grect_inset(bounds, GEdgeInsets(y_margin, x_margin, 0, 0)));
63 | text_layer_set_font(s_title_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD));
64 | text_layer_set_background_color(s_title_layer, GColorClear);
65 | text_layer_set_text_alignment(s_title_layer, GTextAlignmentCenter);
66 | text_layer_set_overflow_mode(s_title_layer, GTextOverflowModeWordWrap);
67 | layer_add_child(window_layer, text_layer_get_layer(s_title_layer));
68 |
69 | const int text_size = 50;
70 | const int y_centered = (bounds.size.h - text_size) / 2;
71 | s_value_layer = text_layer_create(grect_inset(bounds, GEdgeInsets(y_centered, x_margin, 0, 0)));
72 | text_layer_set_font(s_value_layer, fonts_get_system_font(FONT_KEY_BITHAM_42_MEDIUM_NUMBERS));
73 | text_layer_set_background_color(s_value_layer, GColorClear);
74 | text_layer_set_text_alignment(s_value_layer, GTextAlignmentCenter);
75 | text_layer_set_overflow_mode(s_value_layer, GTextOverflowModeWordWrap);
76 | layer_add_child(window_layer, text_layer_get_layer(s_value_layer));
77 |
78 | s_status_bar = status_bar_layer_create();
79 | status_bar_layer_set_colors(s_status_bar, GColorBlack, GColorWhite);
80 | layer_add_child(window_layer, status_bar_layer_get_layer(s_status_bar));
81 | }
82 |
83 | static void window_appear(Window *window) {
84 | // Reset state
85 | s_value = s_min;
86 | text_layer_set_text(s_title_layer, s_title_buffer);
87 | set_value();
88 | }
89 |
90 | static void window_unload(Window *window) {
91 | text_layer_destroy(s_title_layer);
92 | text_layer_destroy(s_value_layer);
93 | action_bar_layer_destroy(s_action_bar);
94 | status_bar_layer_destroy(s_status_bar);
95 |
96 | gbitmap_destroy(s_up_bitmap);
97 | gbitmap_destroy(s_select_bitmap);
98 | gbitmap_destroy(s_down_bitmap);
99 |
100 | window_destroy(s_window);
101 | s_window = NULL;
102 | }
103 |
104 | void new_number_window_push(char *title, int min, int max, int step, NewNumberWindowCallback callback) {
105 | s_callback = callback;
106 | s_min = min;
107 | s_max = max;
108 | s_step = step;
109 | snprintf(s_title_buffer, sizeof(s_title_buffer), "%s", title);
110 |
111 | if(!s_window) {
112 | s_window = window_create();
113 | window_set_window_handlers(s_window, (WindowHandlers) {
114 | .load = window_load,
115 | .unload = window_unload,
116 | .appear = window_appear
117 | });
118 | }
119 | window_stack_push(s_window, true);
120 |
121 | // APP_LOG(APP_LOG_LEVEL_DEBUG, "Heap free: %d", (int)heap_bytes_free()); // Pushing the limits of Aplite
122 | }
123 |
--------------------------------------------------------------------------------
/pebble/src/c/lib/new_number_window/new_number_window.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | typedef void(NewNumberWindowCallback)(int);
6 |
7 | void new_number_window_push(char *title, int min, int max, int step, NewNumberWindowCallback callback);
8 |
--------------------------------------------------------------------------------
/pebble/src/c/main.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "config.h"
4 |
5 | #include "modules/data.h"
6 | #include "modules/comm.h"
7 | #include "modules/sync_timer.h"
8 |
9 | #include "windows/splash_window.h"
10 | #include "windows/list_window.h"
11 |
12 | // --------------------------------- AppGlance ---------------------------------
13 |
14 | #if PBL_API_EXISTS(app_glance_reload)
15 | static void app_glance_callback(AppGlanceReloadSession *session, size_t limit, void *context) {
16 | if(limit < 1) {
17 | return;
18 | }
19 |
20 | // Find next upcoming Event
21 | Event events[MAX_WAKEUPS];
22 | int next_index = -1;
23 | time_t next_timestamp = 2147483647; // Max int32
24 | for(int i = 0; i < MAX_WAKEUPS; i++) {
25 | if(!persist_exists(i)) {
26 | continue;
27 | }
28 |
29 | persist_read_data(i, &events[i], sizeof(Event));
30 | time_t timestamp;
31 | wakeup_query(events[i].wakeup_id, ×tamp);
32 |
33 | if(timestamp < next_timestamp) {
34 | next_timestamp = timestamp;
35 | next_index = i;
36 | }
37 | }
38 |
39 | char buffer[64];
40 | if(next_index == -1) {
41 | // No events found
42 | snprintf(buffer, sizeof(buffer), "No upcoming events");
43 | } else {
44 | int minute = events[next_index].minute;
45 | char min_buff[8];
46 | if(minute < 10) {
47 | snprintf(min_buff, sizeof(min_buff), "0%d", minute);
48 | } else {
49 | snprintf(min_buff, sizeof(min_buff), "%d", minute);
50 | }
51 | snprintf(buffer, sizeof(buffer), "Next event: %s %s at %d:%s",
52 | list_window_get_type_string(events[next_index].type),
53 | wakeup_window_get_state_string(events[next_index].state),
54 | events[next_index].hour, min_buff);
55 |
56 | time_t timestamp;
57 | wakeup_query(events[next_index].wakeup_id, ×tamp);
58 | }
59 | const AppGlanceSlice slice = (AppGlanceSlice) {
60 | .layout = {
61 | .subtitle_template_string = &buffer[0]
62 | },
63 | .expiration_time = APP_GLANCE_SLICE_NO_EXPIRATION
64 | };
65 | app_glance_add_slice(session, slice);
66 | }
67 | #endif
68 |
69 | // ---------------------------------- Wakeup -----------------------------------
70 |
71 | static void wakeup_handler(WakeupId wakeup_id, int32_t cookie) {
72 | vibes_double_pulse();
73 |
74 | // Load event
75 | Event e;
76 | persist_read_data(cookie, &e, sizeof(Event));
77 |
78 | // Wakeup found, carry out action
79 | if(comm_check_bt("Dashboard event failed: watch was disconnected.")) {
80 | comm_request_toggle(e.type, e.state);
81 | }
82 |
83 | // Re-schedule for same time tomorrow
84 | time_t timestamp = clock_to_timestamp(schedule_window_get_tomorrow_weekday(), e.hour, e.minute);
85 | int id = wakeup_schedule(timestamp, cookie, false);
86 |
87 | // Check
88 | if(wakeup_query(id, ×tamp)) {
89 | // Store backing data
90 | e.wakeup_id = id;
91 | persist_write_data(cookie, &e, sizeof(Event));
92 | } else {
93 | APP_LOG(APP_LOG_LEVEL_ERROR, "Wakeup recall failed %d", id);
94 | dialog_window_push("Failed to re-schedule wakeup for tomorrow");
95 | }
96 |
97 | // Show UI
98 | wakeup_window_push(e.type, e.state);
99 | }
100 |
101 | static void begin_handler(void *context) {
102 | if(TEST) {
103 | test_populate_data();
104 | return;
105 | }
106 |
107 | if(!comm_check_bt("Watch disconnected. Reconnect to continue.")) {
108 | return;
109 | }
110 |
111 | // Connect to real device
112 | sync_timer_begin(SYNC_TIMER_INTERVAL_S); // Update regularly while open
113 | comm_request_all(); // Response will contain everything, and use comm
114 | // module timeout mechanism
115 | }
116 |
117 | // ------------------------------------ App ------------------------------------
118 |
119 | static void init() {
120 | comm_init(BUFFER_SIZE_IN, BUFFER_SIZE_OUT);
121 | data_init();
122 | wakeup_service_subscribe(wakeup_handler);
123 |
124 | if(launch_reason() == APP_LAUNCH_WAKEUP) {
125 | // The app was started by a wakeup
126 | WakeupId id = 0;
127 | int32_t reason = 0;
128 |
129 | // Get details and handle the wakeup
130 | wakeup_get_launch_event(&id, &reason);
131 | wakeup_handler(id, reason);
132 | } else {
133 | // No wakeup, launch normally
134 | splash_window_push();
135 | app_timer_register(INITIAL_DELAY, begin_handler, NULL);
136 | }
137 | }
138 |
139 | static void deinit() {
140 | data_deinit();
141 |
142 | #if PBL_API_EXISTS(app_glance_reload)
143 | app_glance_reload(app_glance_callback, NULL);
144 | #endif
145 | }
146 |
147 | int main() {
148 | APP_LOG(APP_LOG_LEVEL_INFO, "Dashboard v%s", VERSION);
149 |
150 | init();
151 | app_event_loop();
152 | deinit();
153 | }
154 |
--------------------------------------------------------------------------------
/pebble/src/c/modules/comm.c:
--------------------------------------------------------------------------------
1 | #include "comm.h"
2 |
3 | static AppTimer *s_timeout_timer;
4 | static bool s_quick_started = false;
5 |
6 | static void inbox_received_handler(DictionaryIterator *iter, void *context) {
7 | if(s_timeout_timer) {
8 | app_timer_cancel(s_timeout_timer);
9 | s_timeout_timer = NULL;
10 | }
11 |
12 | int size = (int)iter->end - (int)iter->dictionary;
13 | if(DEBUG) APP_LOG(APP_LOG_LEVEL_DEBUG, "Dict size: %d", size);
14 |
15 | // Wrong version gate
16 | if(dict_find(iter, ErrorCodeWrongVersion) != NULL) {
17 | APP_LOG(APP_LOG_LEVEL_ERROR, "Wrong version error!");
18 |
19 | dialog_window_push(
20 | PBL_IF_ROUND_ELSE("Update\nwatchapp using the Dashboard Android app!",
21 | "Update watchapp using the Dashboard Android app!")
22 | );
23 | splash_window_remove_from_stack();
24 | return;
25 | }
26 |
27 | // Process the incoming update
28 | if(dict_find(iter, MessageTypeRequestAll) != NULL) {
29 | data_set_gsm_name(dict_find(iter, AppKeyGSMName)->value->cstring);
30 | data_set_wifi_name(dict_find(iter, AppKeyWifiName)->value->cstring);
31 | data_set_gsm_percent(dict_find(iter, AppKeyGSMPercent)->value->int8);
32 | data_set_battery_percent(dict_find(iter, AppKeyBatteryPercent)->value->int8);
33 | data_set_storage_gb_major(dict_find(iter, AppKeyStorageFreeGBMajor)->value->int8);
34 | data_set_storage_gb_minor(dict_find(iter, AppKeyStorageFreeGBMinor)->value->int8);
35 | data_set_storage_percent(dict_find(iter, AppKeyStoragePercent)->value->int8);
36 | data_set_toggle_order(dict_find(iter, AppKeyToggleOrderString)->value->cstring);
37 | data_set_is_lollipop(dict_find(iter, AppKeyIsLollipop)->value->int8 == 1);
38 |
39 | // 'Request all' response
40 | data_set_toggle_state(ToggleTypeWifi, (dict_find(iter, AppKeyToggleWifi)->value->int8 == 1) ? ToggleStateOn : ToggleStateOff);
41 | data_set_toggle_state(ToggleTypeData, (dict_find(iter, AppKeyToggleData)->value->int8 == 1) ? ToggleStateOn : ToggleStateOff);
42 | data_set_toggle_state(ToggleTypeBluetooth, (dict_find(iter, AppKeyToggleBluetooth)->value->int8 == 1) ? ToggleStateOn : ToggleStateOff);
43 | data_set_toggle_state(ToggleTypeWifiAP, (dict_find(iter, AppKeyToggleWifiAP)->value->int8 == 1) ? ToggleStateOn : ToggleStateOff);
44 | data_set_toggle_state(ToggleTypeSync, (dict_find(iter, AppKeyToggleSync)->value->int8 == 1) ? ToggleStateOn : ToggleStateOff);
45 | data_set_toggle_state(ToggleTypeFindPhone, (dict_find(iter, AppKeyToggleFindPhone)->value->int8 == 1) ? ToggleStateOn : ToggleStateOff);
46 | data_set_toggle_state(ToggleTypeRinger, dict_find(iter, AppKeyToggleRinger)->value->int8); // Uses toggle state in encoding
47 | data_set_toggle_state(ToggleTypeAutoBrightness, dict_find(iter, AppKeyToggleAutoBrightness)->value->int8);
48 | data_set_toggle_state(ToggleTypeLockPhone, ToggleStateOff); // Always
49 |
50 | unified_window_push();
51 | unified_window_set_interaction_enabled(true);
52 | splash_window_remove_from_stack();
53 |
54 | // Quick launch?
55 | Tuple *quick_launch_enabled_t = dict_find(iter, AppKeyQuickLaunchEnabled);
56 | if(quick_launch_enabled_t) {
57 | bool enabled = quick_launch_enabled_t->value->int8 == 1;
58 | int type = dict_find(iter, AppKeyQuickLaunchType)->value->int8;
59 |
60 | if(enabled && !s_quick_started) {
61 | s_quick_started = true;
62 | unified_window_jump_to(type);
63 | }
64 | }
65 | }
66 |
67 | // 'Toggle' response
68 | else {
69 | // If response for WiFi AP, request another full sync
70 | for(int key = AppKeyToggleWifi; key < AppKeyToggleOrderString; key++) { // Flaky if something extends the linear list of keys here
71 | Tuple *t = dict_find(iter, key);
72 | if(t) {
73 | int type = key - AppKeyToggleWifi; // Also enum dependent
74 | int8_t new_state = t->value->int8;
75 | if(DEBUG) APP_LOG(APP_LOG_LEVEL_DEBUG, "Received type %d set to %d", type, new_state);
76 |
77 | switch(type) {
78 | // new state encoded
79 | case ToggleTypeData:
80 | if(new_state == ErrorCodeNoRoot) {
81 | dialog_window_push(PBL_IF_ROUND_ELSE(
82 | "Data toggle\nnot available without root.",
83 | "Data toggle not available without root."));
84 | data_set_toggle_state(ToggleTypeData, ToggleStateOff);
85 | } else if(new_state == ErrorCodeDataNotEnabled) {
86 | dialog_window_push(PBL_IF_ROUND_ELSE(
87 | "Data toggle\nnot enabled in Android app.",
88 | "Data toggle not enabled in Android app."));
89 | data_set_toggle_state(ToggleTypeData, ToggleStateOff);
90 | }
91 | break;
92 | case ToggleTypeWifi:
93 | case ToggleTypeSync:
94 | case ToggleTypeWifiAP:
95 | case ToggleTypeFindPhone:
96 | case ToggleTypeAutoBrightness:
97 | case ToggleTypeRinger:
98 | data_set_toggle_state(type, new_state);
99 | break;
100 | // Off only
101 | case ToggleTypeBluetooth:
102 | dialog_window_push("Phone is now disconnected.");
103 | unified_window_remove_from_stack();
104 | splash_window_remove_from_stack();
105 | data_set_toggle_state(type, ToggleStateOff);
106 | break;
107 | case ToggleTypeLockPhone:
108 | if(new_state == ErrorCodeNoDeviceAdmin) {
109 | dialog_window_push("Unavailable without Device Admin permission.");
110 | data_set_toggle_state(type, ToggleStateOff);
111 | } else if(new_state == ErrorCodeLockSuccess) {
112 | data_set_toggle_state(type, ToggleStateLocked);
113 | }
114 | break;
115 | }
116 | } else {
117 | if(DEBUG) APP_LOG(APP_LOG_LEVEL_ERROR, "AppKey %d was not present", key);
118 | }
119 | }
120 | }
121 |
122 | unified_window_reload();
123 | }
124 |
125 | static void timeout_handler(void *context) {
126 | APP_LOG(APP_LOG_LEVEL_ERROR, "Timed out. Retrying...");
127 | comm_request_all();
128 | }
129 |
130 | static void init_timeout_timer() {
131 | s_timeout_timer = app_timer_register(COMM_TIMEOUT_MS, timeout_handler, NULL);
132 | }
133 |
134 | bool comm_check_bt(char *failed_notice) {
135 | if(!connection_service_peek_pebble_app_connection()) {
136 | dialog_window_push(failed_notice);
137 | splash_window_remove_from_stack();
138 | unified_window_remove_from_stack();
139 | return false;
140 | } else {
141 | return true;
142 | }
143 | }
144 |
145 | void comm_request_toggle(int type, int state) {
146 | if(TEST) {
147 | dialog_window_push("Cannot toggle in test mode");
148 | return;
149 | }
150 |
151 | if(!comm_check_bt("Watch disconnected. Reconnect to continue.")) {
152 | return;
153 | }
154 |
155 | // Transform type from type enum value to key enum value (they are linear)
156 | type += AppKeyToggleWifi;
157 |
158 | DictionaryIterator *iter;
159 | AppMessageResult result = app_message_outbox_begin(&iter);
160 | if(result != APP_MSG_OK) {
161 | APP_LOG(APP_LOG_LEVEL_ERROR, "Failed to open outbox: %d", (int)result);
162 | }
163 |
164 | const int dummy = 0;
165 | dict_write_int(iter, MessageTypeToggle, &dummy, sizeof(int), true);
166 | dict_write_int(iter, type, &state, sizeof(int), true);
167 |
168 | result = app_message_outbox_send();
169 | if(result != APP_MSG_OK) {
170 | APP_LOG(APP_LOG_LEVEL_ERROR, "Error sending outbox: %d", (int)result);
171 | }
172 | }
173 |
174 | void comm_request_all() {
175 | if(TEST) {
176 | return;
177 | }
178 |
179 | if(!comm_check_bt("Watch disconnected. Reconnect to continue.")) {
180 | return;
181 | }
182 |
183 | // Just in case, but first time should be immediately after version check
184 | app_message_register_inbox_received(inbox_received_handler);
185 |
186 | // Can't use pebble-packet due to low memory on Aplite
187 | DictionaryIterator *iter;
188 | AppMessageResult result = app_message_outbox_begin(&iter);
189 | if(result != APP_MSG_OK) {
190 | APP_LOG(APP_LOG_LEVEL_ERROR, "Failed to open outbox: %d", (int)result);
191 | return;
192 | }
193 |
194 | const int dummy = 0;
195 | dict_write_int(iter, MessageTypeRequestAll, &dummy, sizeof(int), true);
196 |
197 | // Add version instead of version_check library
198 | dict_write_cstring(iter, AppKeyVersion, VERSION);
199 |
200 | result = app_message_outbox_send();
201 | if(result != APP_MSG_OK) {
202 | APP_LOG(APP_LOG_LEVEL_ERROR, "Error sending outbox: %d", (int)result);
203 | } else {
204 | init_timeout_timer();
205 | }
206 | }
207 |
208 | void comm_init(uint32_t inbox, uint32_t outbox) {
209 | app_message_open(inbox, outbox);
210 | }
211 |
--------------------------------------------------------------------------------
/pebble/src/c/modules/comm.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "../types.h"
6 | #include "../config.h"
7 |
8 | #include "../util/test.h"
9 |
10 | #include "../windows/dialog_window.h"
11 | #include "../windows/splash_window.h"
12 | #include "../windows/unified_window.h"
13 |
14 | void comm_init(uint32_t inbox, uint32_t outbox);
15 |
16 | void comm_request_all();
17 |
18 | void comm_request_toggle(int type, int state);
19 |
20 | bool comm_check_bt(char *failed_notice);
21 |
--------------------------------------------------------------------------------
/pebble/src/c/modules/data.c:
--------------------------------------------------------------------------------
1 | #include "data.h"
2 |
3 | // Data model
4 | typedef struct {
5 | int type;
6 | int state;
7 | } Toggle;
8 |
9 | static Toggle s_toggles[ToggleTypeCount];
10 | static char s_gsm_name[NETWORK_MAX_LENGTH];
11 | static char s_wifi_name[NETWORK_MAX_LENGTH];
12 | static char s_order[ToggleTypeCount];
13 | static int s_gsm_percent, s_battery_percent, s_storage_gb_major, s_storage_gb_minor, s_storage_percent;
14 | static bool s_is_lollipop;
15 |
16 | void data_init() {
17 | for(int i = 0; i < ToggleTypeCount; i++) {
18 | s_toggles[i].type = i;
19 | s_toggles[i].state = ToggleStateWaiting;
20 | }
21 |
22 | snprintf(s_gsm_name, sizeof(s_gsm_name), "-");
23 | snprintf(s_wifi_name, sizeof(s_wifi_name), "-");
24 | }
25 |
26 | void data_deinit() { }
27 |
28 | void data_set_toggle_type_at_index(int index, int type) {
29 | s_toggles[index].type = type;
30 | }
31 |
32 | int data_get_toggle_type_at_index(int index) {
33 | return s_toggles[index].type;
34 | }
35 |
36 | void data_set_toggle_state(int type, int state) {
37 | int index = 0;
38 | while(s_toggles[index].type != type) {
39 | index++;
40 |
41 | if(index == ToggleTypeCount) {
42 | APP_LOG(APP_LOG_LEVEL_ERROR, "No toggle found with type %d!", type);
43 | }
44 | }
45 | s_toggles[index].state = state;
46 | }
47 |
48 | int data_get_toggle_state(int type) {
49 | int index = 0;
50 | while(s_toggles[index].type != type) {
51 | index++;
52 |
53 | if(index == ToggleTypeCount) {
54 | APP_LOG(APP_LOG_LEVEL_ERROR, "No toggle found with type %d!", type);
55 | }
56 | }
57 | return s_toggles[index].state;
58 | }
59 |
60 | void data_set_gsm_name(char *name) {
61 | snprintf(s_gsm_name, sizeof(s_gsm_name), "%s", name);
62 | }
63 |
64 | char* data_get_gsm_name() {
65 | return &s_gsm_name[0];
66 | }
67 |
68 | void data_set_wifi_name(char *name) {
69 | snprintf(s_wifi_name, sizeof(s_wifi_name), "%s", name);
70 | }
71 |
72 | char* data_get_wifi_name() {
73 | return &s_wifi_name[0];
74 | }
75 |
76 | void data_set_gsm_percent(int percent) {
77 | s_gsm_percent = percent;
78 | }
79 |
80 | int data_get_gsm_percent() {
81 | return s_gsm_percent;
82 | }
83 |
84 | void data_set_battery_percent(int percent) {
85 | s_battery_percent = percent;
86 | }
87 |
88 | int data_get_battery_percent() {
89 | return s_battery_percent;
90 | }
91 |
92 | void data_set_storage_gb_major(int value) {
93 | s_storage_gb_major = value;
94 | }
95 |
96 | int data_get_storage_gb_major() {
97 | return s_storage_gb_major;
98 | }
99 |
100 | void data_set_storage_gb_minor(int value) {
101 | s_storage_gb_minor = value;
102 | }
103 |
104 | int data_get_storage_gb_minor() {
105 | return s_storage_gb_minor;
106 | }
107 |
108 | void data_set_is_lollipop(bool is_lollipop) {
109 | s_is_lollipop = is_lollipop;
110 | persist_write_bool(PersistKeyIsLollipop, is_lollipop);
111 | }
112 |
113 | bool data_get_is_lollipop() {
114 | return false; // Ignore for now. Is it even a thing anymore?
115 | // return s_is_lollipop;
116 | }
117 |
118 | static int char2int(char c) {
119 | return (int)(c - 48);
120 | }
121 |
122 | void data_set_toggle_order(char *order) {
123 | snprintf(s_order, sizeof(s_order), "%s", order);
124 | for(int i = 0; i < ToggleTypeCount; i++) {
125 | s_toggles[i].type = char2int(order[i]);
126 | s_toggles[i].state = ToggleStateWaiting;
127 |
128 | if(DEBUG) APP_LOG(APP_LOG_LEVEL_DEBUG, "Set s_toggles[%d] to type %d", i, s_toggles[i].type);
129 | }
130 | }
131 |
132 | char* data_get_toggle_order() {
133 | return &s_order[0];
134 | }
135 |
136 | void data_set_storage_percent(int perc) {
137 | s_storage_percent = perc;
138 | }
139 |
140 | int data_get_storage_percent() {
141 | return s_storage_percent;
142 | }
143 |
144 | int data_get_toggle_index_with_type(int type) {
145 | int index = 0;
146 | while(s_toggles[index].type != type) {
147 | index++;
148 |
149 | if(index == ToggleTypeCount) {
150 | APP_LOG(APP_LOG_LEVEL_ERROR, "No toggle found with type %d!", type);
151 | }
152 | }
153 | return index;
154 | }
155 |
--------------------------------------------------------------------------------
/pebble/src/c/modules/data.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "../config.h"
6 | #include "../types.h"
7 |
8 | void data_init();
9 | void data_deinit();
10 |
11 | void data_set_toggle_state(int type, int state);
12 | int data_get_toggle_state(int type);
13 |
14 | void data_set_toggle_type_at_index(int index, int type);
15 | int data_get_toggle_type_at_index(int index);
16 |
17 | int data_get_toggle_index_with_type(int type);
18 |
19 | void data_set_gsm_name(char *name);
20 | char* data_get_gsm_name();
21 |
22 | void data_set_wifi_name(char *name);
23 | char* data_get_wifi_name();
24 |
25 | void data_set_gsm_percent(int percent);
26 | int data_get_gsm_percent();
27 |
28 | void data_set_battery_percent(int percent);
29 | int data_get_battery_percent();
30 |
31 | void data_set_storage_gb_major(int value);
32 | int data_get_storage_gb_major();
33 |
34 | void data_set_storage_gb_minor(int value);
35 | int data_get_storage_gb_minor();
36 |
37 | void data_set_is_lollipop(bool is_lollipop);
38 | bool data_get_is_lollipop();
39 |
40 | void data_set_toggle_order(char *order);
41 | char* data_get_toggle_order();
42 |
43 | void data_set_storage_percent(int perc);
44 | int data_get_storage_percent();
45 |
--------------------------------------------------------------------------------
/pebble/src/c/modules/icons.c:
--------------------------------------------------------------------------------
1 | #include "icons.h"
2 |
3 | #define NUM_ICONS 11
4 |
5 | static GBitmap *s_bitmaps[NUM_ICONS];
6 |
7 | void icons_load() {
8 | s_bitmaps[0] = gbitmap_create_with_resource(RESOURCE_ID_WIFI);
9 | s_bitmaps[1] = gbitmap_create_with_resource(RESOURCE_ID_DATA);
10 | s_bitmaps[2] = gbitmap_create_with_resource(RESOURCE_ID_BLUETOOTH);
11 | s_bitmaps[3] = gbitmap_create_with_resource(RESOURCE_ID_RINGER_LOUD);
12 | s_bitmaps[4] = gbitmap_create_with_resource(RESOURCE_ID_RINGER_VIBRATE);
13 |
14 | if(data_get_is_lollipop()) {
15 | s_bitmaps[5] = gbitmap_create_with_resource(RESOURCE_ID_RINGER_PRIORITY);
16 | } else {
17 | s_bitmaps[5] = gbitmap_create_with_resource(RESOURCE_ID_RINGER_SILENT);
18 | }
19 |
20 | s_bitmaps[6] = gbitmap_create_with_resource(RESOURCE_ID_SYNC);
21 | s_bitmaps[7] = gbitmap_create_with_resource(RESOURCE_ID_WIFI_AP);
22 | s_bitmaps[8] = gbitmap_create_with_resource(RESOURCE_ID_FIND_PHONE);
23 | s_bitmaps[9] = gbitmap_create_with_resource(RESOURCE_ID_LOCK_PHONE);
24 | s_bitmaps[10] = gbitmap_create_with_resource(RESOURCE_ID_BRIGHTNESS);
25 |
26 | // Check integrity
27 | for(int i = 0; i < NUM_ICONS; i++) {
28 | if(s_bitmaps[i] == NULL) {
29 | APP_LOG(APP_LOG_LEVEL_ERROR, "Failed to allocate bitmap %d", i);
30 | }
31 | }
32 | }
33 |
34 | void icons_unload() {
35 | for(int i = 0; i < NUM_ICONS; i++) {
36 | if(s_bitmaps[i]) {
37 | gbitmap_destroy(s_bitmaps[i]);
38 | }
39 | }
40 | }
41 |
42 | GBitmap* icons_get_with_type(int type) {
43 | switch(type) {
44 | case ToggleTypeWifi: return s_bitmaps[0];
45 | case ToggleTypeData: return s_bitmaps[1];
46 | case ToggleTypeBluetooth: return s_bitmaps[2];
47 | case ToggleTypeRinger: {
48 | int ringer_state = data_get_toggle_state(ToggleTypeRinger);
49 | switch(ringer_state) {
50 | case ToggleStateLoud: return s_bitmaps[3];
51 | case ToggleStateVibrate: return s_bitmaps[4];
52 | case ToggleStateSilent: return s_bitmaps[5];
53 | default:
54 | APP_LOG(APP_LOG_LEVEL_ERROR, "Unknown icons_get_with_type() for ringer state %d", ringer_state);
55 | return s_bitmaps[8]; // '?'
56 | }
57 | } break;
58 | case ToggleTypeSync: return s_bitmaps[6];
59 | case ToggleTypeWifiAP: return s_bitmaps[7];
60 | case ToggleTypeFindPhone: return s_bitmaps[8];
61 | case ToggleTypeLockPhone: return s_bitmaps[9];
62 | case ToggleTypeAutoBrightness: return s_bitmaps[10];
63 |
64 | default:
65 | APP_LOG(APP_LOG_LEVEL_ERROR, "Unknown icons_get_with_type() for type %d", type);
66 | return NULL;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/pebble/src/c/modules/icons.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "data.h"
6 |
7 | void icons_load();
8 |
9 | void icons_unload();
10 |
11 | GBitmap* icons_get_with_type(int type);
12 |
--------------------------------------------------------------------------------
/pebble/src/c/modules/sync_timer.c:
--------------------------------------------------------------------------------
1 | #include "sync_timer.h"
2 |
3 | static AppTimer *s_timer;
4 | static int s_interval_ms;
5 |
6 | static void timer_handler(void *context);
7 |
8 | static void re_register() {
9 | s_timer = app_timer_register(s_interval_ms, timer_handler, NULL);
10 | }
11 |
12 | static void timer_handler(void *context) {
13 | s_timer = NULL;
14 | comm_request_all();
15 | re_register();
16 | if(DEBUG) APP_LOG(APP_LOG_LEVEL_DEBUG, "Resynchronizing...");
17 | }
18 |
19 | void sync_timer_begin(int interval_s) {
20 | s_interval_ms = interval_s * 1000;
21 | re_register();
22 | }
23 |
--------------------------------------------------------------------------------
/pebble/src/c/modules/sync_timer.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "comm.h"
6 |
7 | void sync_timer_begin(int interval_s);
8 |
--------------------------------------------------------------------------------
/pebble/src/c/types.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | // TODO: Pebble packet schema and callbacks library
6 | typedef enum {
7 | AppKeyGSMName = 0,
8 | AppKeyGSMPercent,
9 | AppKeyWifiName,
10 | AppKeyBatteryPercent,
11 | AppKeyStorageFreeGBMajor,
12 | AppKeyStorageFreeGBMinor,
13 | AppKeyToggleWifi, // Begin iterable sequence
14 | AppKeyToggleData, //
15 | AppKeyToggleBluetooth, //
16 | AppKeyToggleRinger, //
17 | AppKeyToggleSync, //
18 | AppKeyToggleWifiAP, //
19 | AppKeyToggleFindPhone, //
20 | AppKeyToggleLockPhone, //
21 | AppKeyToggleAutoBrightness, //
22 | AppKeyToggleOrderString, // End iterable sequence (non-inclusive)
23 | AppKeyIsLollipop,
24 | AppKeyStoragePercent,
25 | AppKeyQuickLaunchEnabled,
26 | AppKeyQuickLaunchType,
27 | AppKeyVersion
28 | } AppKey; // KEEP IN ORDER
29 |
30 | typedef enum {
31 | MessageTypeRequestAll = 543,
32 | MessageTypeToggle
33 | } MessageType;
34 |
35 | // Must stay linear for data_init()!
36 | typedef enum {
37 | ToggleTypeWifi = 0,
38 | ToggleTypeData,
39 | ToggleTypeBluetooth,
40 | ToggleTypeRinger,
41 | ToggleTypeSync,
42 | ToggleTypeWifiAP,
43 | ToggleTypeFindPhone,
44 | ToggleTypeLockPhone,
45 | ToggleTypeAutoBrightness,
46 |
47 | ToggleTypeCount
48 | } ToggleType;
49 |
50 | typedef enum {
51 | ToggleStateWaiting = 0,
52 | ToggleStateOff,
53 | ToggleStateOn,
54 | ToggleStateLoud,
55 | ToggleStateVibrate,
56 | ToggleStateSilent,
57 | ToggleStateBrightnessAutomatic,
58 | ToggleStateBrightnessManual,
59 | ToggleStateLocked
60 | } ToggleState;
61 |
62 | typedef enum {
63 | ErrorCodeNoRoot = 0,
64 | ErrorCodeNoDeviceAdmin,
65 | ErrorCodeLockSuccess,
66 | ErrorCodeDataNotEnabled,
67 | ErrorCodeWrongVersion = 40
68 | } ErrorCode;
69 |
70 | typedef struct {
71 | int type;
72 | int state;
73 | int hour;
74 | int minute;
75 | int set;
76 | int wakeup_id;
77 | } Event;
78 | // List row keys are row index
79 |
80 | typedef enum {
81 | PersistKeyIsLollipop = 453786
82 | } PersistKey;
--------------------------------------------------------------------------------
/pebble/src/c/util/animation.c:
--------------------------------------------------------------------------------
1 | #include "animation.h"
2 |
3 | static bool s_locked;
4 |
5 | void animation_animate_layer(Layer *layer, GRect start, GRect finish, int duration_ms, AnimationHandlers *handlers) {
6 | PropertyAnimation *prop_anim = property_animation_create_layer_frame(layer, &start, &finish);
7 | Animation *anim = property_animation_get_animation(prop_anim);
8 | animation_set_duration(anim, duration_ms);
9 | if(handlers) {
10 | animation_set_handlers(anim, (*handlers), NULL);
11 | }
12 | animation_schedule(anim);
13 | }
14 |
15 | void animation_animate_delta_y(Layer *layer, int dy, int duration_ms, AnimationHandlers *handlers) {
16 | GRect start = layer_get_frame(layer);
17 | GRect finish = GRect(start.origin.x, start.origin.y + dy, start.size.w, start.size.h);
18 | animation_animate_layer(layer, start, finish, duration_ms, handlers);
19 | }
20 |
21 | void animation_lock() {
22 | s_locked = true;
23 | }
24 |
25 | void animation_unlock() {
26 | s_locked = false;
27 | }
28 |
29 | bool animation_is_locked() {
30 | return s_locked;
31 | }
--------------------------------------------------------------------------------
/pebble/src/c/util/animation.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | void animation_animate_layer(Layer *layer, GRect start, GRect finish, int duration, AnimationHandlers *handlers);
6 |
7 | void animation_animate_delta_y(Layer *layer, int dy, int duration, AnimationHandlers *handlers);
8 |
9 | void animation_lock();
10 |
11 | void animation_unlock();
12 |
13 | bool animation_is_locked();
14 |
--------------------------------------------------------------------------------
/pebble/src/c/util/test.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | // Use local data in emulator until PKA is emulated
4 | void test_populate_data() {
5 | data_set_toggle_order("012345678");
6 | data_set_is_lollipop(true);
7 |
8 | data_set_toggle_state(ToggleTypeWifi, ToggleStateOn);
9 | data_set_toggle_state(ToggleTypeData, ToggleStateOff);
10 | data_set_toggle_state(ToggleTypeBluetooth, ToggleStateOn);
11 | data_set_toggle_state(ToggleTypeRinger, ToggleStateSilent);
12 | data_set_toggle_state(ToggleTypeSync, ToggleStateOn);
13 | data_set_toggle_state(ToggleTypeWifiAP, ToggleStateOn);
14 | data_set_toggle_state(ToggleTypeFindPhone, ToggleStateOn);
15 | data_set_toggle_state(ToggleTypeLockPhone, ToggleStateWaiting);
16 | data_set_toggle_state(ToggleTypeAutoBrightness, ToggleStateBrightnessAutomatic);
17 |
18 | data_set_gsm_name("T-Mobile");
19 | data_set_wifi_name("BTHub3-NCNR");
20 | data_set_gsm_percent(50);
21 | data_set_battery_percent(75);
22 | data_set_storage_gb_major(16);
23 | data_set_storage_gb_minor(2);
24 | data_set_storage_percent(65);
25 |
26 | unified_window_push();
27 | unified_window_set_interaction_enabled(true);
28 | splash_window_remove_from_stack();
29 | }
30 |
--------------------------------------------------------------------------------
/pebble/src/c/util/test.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "../modules/data.h"
6 |
7 | #include "../windows/splash_window.h"
8 | #include "../windows/unified_window.h"
9 |
10 | void test_populate_data();
11 |
--------------------------------------------------------------------------------
/pebble/src/c/windows/dialog_window.c:
--------------------------------------------------------------------------------
1 | // TODO: Make pebble package
2 | #include "dialog_window.h"
3 |
4 | static Window *s_window;
5 | static TextLayer *s_text_layer;
6 | static ActionBarLayer *s_action_bar_layer;
7 |
8 | static GBitmap *s_tick_bitmap;
9 |
10 | #define MAX_LENGTH 128
11 | static char s_text_buffer[MAX_LENGTH];
12 |
13 | static void update_layout() {
14 | Layer *window_layer = window_get_root_layer(s_window);
15 | GRect bounds = layer_get_bounds(window_layer);
16 |
17 | const int margin = 5;
18 | const int y_margin = PBL_IF_ROUND_ELSE(5 * margin, margin);
19 | GRect text_bounds = grect_inset(bounds, GEdgeInsets(y_margin, ACTION_BAR_WIDTH + margin, margin, margin));
20 | layer_set_frame(text_layer_get_layer(s_text_layer), text_bounds);
21 | }
22 |
23 | static void select_click_handler(ClickRecognizerRef recognizer, void *context) {
24 | window_stack_pop(true);
25 | }
26 |
27 | static void click_config_provider(void *context) {
28 | window_single_click_subscribe(BUTTON_ID_SELECT, select_click_handler);
29 | }
30 |
31 | static void window_load(Window *window) {
32 | Layer *window_layer = window_get_root_layer(window);
33 | GRect bounds = layer_get_bounds(window_layer);
34 |
35 | s_text_layer = text_layer_create(bounds);
36 | text_layer_set_background_color(s_text_layer, GColorClear);
37 | text_layer_set_text_alignment(s_text_layer, PBL_IF_ROUND_ELSE(GTextAlignmentRight, GTextAlignmentLeft));
38 | text_layer_set_font(s_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD));
39 | layer_add_child(window_layer, text_layer_get_layer(s_text_layer));
40 |
41 | s_tick_bitmap = gbitmap_create_with_resource(RESOURCE_ID_TICK);
42 |
43 | s_action_bar_layer = action_bar_layer_create();
44 | action_bar_layer_set_icon(s_action_bar_layer, BUTTON_ID_SELECT, s_tick_bitmap);
45 | action_bar_layer_set_click_config_provider(s_action_bar_layer, click_config_provider);
46 | action_bar_layer_add_to_window(s_action_bar_layer, window);
47 | }
48 |
49 | static void window_unload(Window *window) {
50 | text_layer_destroy(s_text_layer);
51 | action_bar_layer_destroy(s_action_bar_layer);
52 | gbitmap_destroy(s_tick_bitmap);
53 |
54 | window_destroy(s_window);
55 | s_window = NULL;
56 | APP_LOG(APP_LOG_LEVEL_DEBUG, "Dialog window exited with heap free %d", (int)heap_bytes_free());
57 | }
58 |
59 | /************************************ API *************************************/
60 |
61 | // Text should be per-platform newlined as appropriate
62 | void dialog_window_push(char *text) {
63 | if(!s_window) {
64 | s_window = window_create();
65 | window_set_window_handlers(s_window, (WindowHandlers) {
66 | .load = window_load,
67 | .unload = window_unload
68 | });
69 | }
70 | window_stack_push(s_window, true);
71 |
72 | window_set_background_color(s_window, PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite));
73 | text_layer_set_text_color(s_text_layer, GColorBlack);
74 |
75 | snprintf(s_text_buffer, sizeof(s_text_buffer), "%s", text);
76 | text_layer_set_text(s_text_layer, s_text_buffer);
77 |
78 | update_layout();
79 | }
80 |
--------------------------------------------------------------------------------
/pebble/src/c/windows/dialog_window.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | void dialog_window_push(char *text);
6 |
--------------------------------------------------------------------------------
/pebble/src/c/windows/list_window.c:
--------------------------------------------------------------------------------
1 | #include "list_window.h"
2 |
3 | #define BUFFER_LENGTH 32
4 | #define ALL_EVENTS -1
5 |
6 | static Window *s_window;
7 | static MenuLayer *s_menu_layer;
8 | static StatusBarLayer *s_status_layer;
9 |
10 | static Event events[MAX_WAKEUPS];
11 | static char list_buffers[MAX_WAKEUPS][BUFFER_LENGTH];
12 |
13 | char* list_window_get_type_string(int type) {
14 | switch(type) {
15 | case ToggleTypeWifi: return "Wi-Fi";
16 | case ToggleTypeData: return "Data";
17 | case ToggleTypeBluetooth: return "Bluetooth";
18 | case ToggleTypeRinger: return "Ringer";
19 | case ToggleTypeSync: return "Auto Sync";
20 | case ToggleTypeWifiAP: return "Hotspot";
21 | case ToggleTypeFindPhone: return "Find Phone";
22 | case ToggleTypeAutoBrightness: return "Auto Bright.";
23 | case ToggleTypeLockPhone: return "Lock Phone";
24 | default:
25 | APP_LOG(APP_LOG_LEVEL_ERROR, "Unknown type for list_window_get_type_string: %d", type);
26 | return "Unknown";
27 | }
28 | }
29 |
30 | static void reload_events(int specific) {
31 | if(specific != ALL_EVENTS) {
32 | // Reload just one
33 | if(persist_exists(specific)) {
34 | persist_read_data(specific, &events[specific], sizeof(Event));
35 | } else {
36 | events[specific].set = 0; // Not valid
37 | }
38 | } else {
39 | // Reload them all
40 | for(int i = 0; i < MAX_WAKEUPS; i++) {
41 | // If this row's event exists...
42 | if(persist_exists(i)) {
43 | // Load it
44 | persist_read_data(i, &events[i], sizeof(Event));
45 |
46 | if(DEBUG) {
47 | // Log it
48 | time_t timestamp;
49 | APP_LOG(APP_LOG_LEVEL_DEBUG, "Read index %d, %d: %s (%d:%d): valid: %s",
50 | i, events[i].type, wakeup_window_get_state_string(events[i].state), events[i].hour,
51 | events[i].minute, (wakeup_query(events[i].wakeup_id, ×tamp) ? "true" : "false")
52 | );
53 | }
54 | } else {
55 | // Does not yet exist
56 | events[i].set = 0;
57 | if(DEBUG) APP_LOG(APP_LOG_LEVEL_DEBUG, "Event %d is not set or does not exist", i);
58 | }
59 | }
60 | }
61 | }
62 |
63 | static int get_total_occupied_rows() {
64 | int result = 0;
65 | for(int i = 0; i < MAX_WAKEUPS; i++) {
66 | result += events[i].set; // 1 or 0, neat
67 | }
68 | return result;
69 | }
70 |
71 | /*********************************** MenuLayer ********************************/
72 |
73 | static void draw_row_callback(GContext *ctx, const Layer *cell_layer, MenuIndex *cell_index, void *context) {
74 | GRect bounds = layer_get_bounds(cell_layer);
75 | int row = cell_index->row;
76 |
77 | if(events[row].set == 0) {
78 | // Not yet set, unused
79 | if(menu_layer_is_index_selected(s_menu_layer, cell_index)) {
80 | menu_cell_basic_draw(ctx, cell_layer, "Unused", "Click to add event...", NULL);\
81 | } else {
82 | menu_cell_basic_draw(ctx, cell_layer, "Unused", NULL, NULL);\
83 | }
84 | } else {
85 | // Used, load the data
86 | int type = events[row].type;
87 | int state = events[row].state;
88 | int hour = events[row].hour;
89 | int minute = events[row].minute;
90 |
91 | if(menu_layer_is_index_selected(s_menu_layer, cell_index)) {
92 | // Show all the details (TODO: padding function)
93 | if(hour < 10 && minute < 10) {
94 | snprintf(list_buffers[row], BUFFER_LENGTH, "%s: %s\n(0%d:0%d)", list_window_get_type_string(type),
95 | wakeup_window_get_state_string(state), hour, minute);
96 | } else if(hour < 10 && minute > 9) {
97 | snprintf(list_buffers[row], BUFFER_LENGTH, "%s: %s\n(0%d:%d)", list_window_get_type_string(type),
98 | wakeup_window_get_state_string(state), hour, minute);
99 | } else if(hour > 9 && minute < 10) {
100 | snprintf(list_buffers[row], BUFFER_LENGTH, "%s: %s\n(%d:0%d)", list_window_get_type_string(type),
101 | wakeup_window_get_state_string(state), hour, minute);
102 | } else {
103 | snprintf(list_buffers[row], BUFFER_LENGTH, "%s: %s\n(%d:%d)", list_window_get_type_string(type),
104 | wakeup_window_get_state_string(state), hour, minute);
105 | }
106 | } else {
107 | // Show minimal detail
108 | snprintf(list_buffers[row], BUFFER_LENGTH, "%s: %s", list_window_get_type_string(type),
109 | wakeup_window_get_state_string(state));
110 | }
111 |
112 | int y_margin = 0;
113 | GRect text_rect = grect_inset(bounds, GEdgeInsets(y_margin, 0, 0, PBL_IF_ROUND_ELSE(0, 5)));
114 | graphics_draw_text(ctx, list_buffers[row], fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD), text_rect,
115 | GTextOverflowModeWordWrap, PBL_IF_ROUND_ELSE(GTextAlignmentCenter, GTextAlignmentLeft), NULL);
116 | text_rect.origin.y += 37;
117 | graphics_draw_text(ctx, "Click to delete...", fonts_get_system_font(FONT_KEY_GOTHIC_18), text_rect,
118 | GTextOverflowModeWordWrap, PBL_IF_ROUND_ELSE(GTextAlignmentCenter, GTextAlignmentLeft), NULL);
119 | }
120 | }
121 |
122 | static int16_t get_cell_height_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, void *context) {
123 | return menu_layer_is_index_selected(menu_layer, cell_index) ?
124 | (3 * MENU_CELL_ROUND_FOCUSED_TALL_CELL_HEIGHT) / 4 : MENU_CELL_ROUND_UNFOCUSED_TALL_CELL_HEIGHT;
125 | }
126 |
127 | static uint16_t num_rows_callback(struct MenuLayer *menu_layer, uint16_t section_index, void *context) {
128 | return MAX_WAKEUPS; // TODO revisit collapsible list structure
129 | }
130 |
131 | static void select_click_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, void *context) {
132 | int row = cell_index->row;
133 |
134 | // Event for this row?
135 | if(persist_read_data(row, &events[row], sizeof(Event)) <= 0) {
136 | // Does not exist, add it with user input
137 | schedule_window_push(row);
138 | } else {
139 | // Exists, remove wakeup and delete data
140 | wakeup_cancel(events[row].wakeup_id);
141 | persist_delete(row);
142 | dialog_window_push("Event deleted.");
143 | }
144 |
145 | // If none remain
146 | if(get_total_occupied_rows() == 0) {
147 | // Cancel All Wakeups
148 | wakeup_cancel_all();
149 | if(DEBUG) APP_LOG(APP_LOG_LEVEL_DEBUG, "Cancelling all wakeups...");
150 |
151 | for(int i = 0; i < MAX_WAKEUPS; i++) {
152 | if(persist_read_data(i, &events[i], sizeof(Event)) > 0) {
153 | // Save and reload only this event
154 | persist_delete(i);
155 | }
156 | }
157 | }
158 |
159 | // Update view
160 | reload_events(ALL_EVENTS);
161 | menu_layer_reload_data(menu_layer);
162 | }
163 |
164 | static uint16_t get_num_sections_callback(struct MenuLayer *menu_layer, void *context) {
165 | const int num_sections = 1;
166 | return num_sections;
167 | }
168 |
169 | /************************************ Window **********************************/
170 |
171 | static void window_appear(Window *window) {
172 | // Load once all events
173 | reload_events(ALL_EVENTS);
174 | menu_layer_reload_data(s_menu_layer);
175 | }
176 |
177 | static void window_load(Window *window) {
178 | Layer *window_layer = window_get_root_layer(window);
179 | GRect bounds = layer_get_bounds(window_layer);
180 |
181 | s_menu_layer = menu_layer_create(grect_inset(bounds, GEdgeInsets(PBL_IF_ROUND_ELSE(0, STATUS_BAR_LAYER_HEIGHT), 0, 0, 0)));
182 | menu_layer_set_center_focused(s_menu_layer, true);
183 | menu_layer_pad_bottom_enable(s_menu_layer, false);
184 | #if defined(PBL_COLOR)
185 | menu_layer_set_normal_colors(s_menu_layer, GColorBlack, GColorWhite);
186 | menu_layer_set_highlight_colors(s_menu_layer, GColorDarkCandyAppleRed, GColorWhite);
187 | #endif
188 | menu_layer_set_click_config_onto_window(s_menu_layer, window);
189 | menu_layer_set_callbacks(s_menu_layer, NULL, (MenuLayerCallbacks) {
190 | .draw_row = draw_row_callback,
191 | .get_cell_height = get_cell_height_callback,
192 | .get_num_rows = num_rows_callback,
193 | .select_click = select_click_callback,
194 | .get_num_sections = get_num_sections_callback
195 | });
196 | layer_add_child(window_layer, menu_layer_get_layer(s_menu_layer));
197 |
198 | s_status_layer = status_bar_layer_create();
199 | status_bar_layer_set_separator_mode(s_status_layer, StatusBarLayerSeparatorModeDotted);
200 | status_bar_layer_set_colors(s_status_layer,
201 | PBL_IF_COLOR_ELSE(GColorDarkCandyAppleRed, GColorWhite), PBL_IF_COLOR_ELSE(GColorWhite, GColorBlack));
202 | layer_add_child(window_layer, status_bar_layer_get_layer(s_status_layer));
203 | }
204 |
205 | static void window_unload(Window *window) {
206 | menu_layer_destroy(s_menu_layer);
207 | status_bar_layer_destroy(s_status_layer);
208 |
209 | window_destroy(window);
210 | s_window = NULL;
211 | }
212 |
213 | void list_window_push() {
214 | if(!s_window) {
215 | s_window = window_create();
216 | window_set_background_color(s_window, PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite));
217 | window_set_window_handlers(s_window, (WindowHandlers) {
218 | .load = window_load,
219 | .unload = window_unload,
220 | .appear = window_appear
221 | });
222 | }
223 | window_stack_push(s_window, true);
224 | }
225 |
--------------------------------------------------------------------------------
/pebble/src/c/windows/list_window.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "dialog_window.h"
4 | #include "schedule_window.h"
5 | #include "wakeup_window.h"
6 |
7 | #include "../config.h"
8 | #include "../types.h"
9 |
10 | char* list_window_get_type_string(int type); // TODO Decouple and move to types.h
11 |
12 | void list_window_push();
13 |
--------------------------------------------------------------------------------
/pebble/src/c/windows/schedule_window.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "dialog_window.h"
6 | #include "list_window.h"
7 |
8 | #include "../config.h"
9 | #include "../types.h"
10 |
11 | #include "../modules/data.h"
12 |
13 | #include "../lib/new_number_window/new_number_window.h"
14 |
15 | WeekDay schedule_window_get_tomorrow_weekday(); // TODO move to types.h
16 |
17 | void schedule_window_push(int position);
18 |
--------------------------------------------------------------------------------
/pebble/src/c/windows/splash_window.c:
--------------------------------------------------------------------------------
1 | #include "splash_window.h"
2 |
3 | #define INTERVAL_MS 150 // Animation frame interval
4 |
5 | static Window *s_window;
6 | static BitmapLayer *s_logo_layer;
7 |
8 | static GBitmap *s_logo_bitmap;
9 |
10 | /*********************************** Window ***********************************/
11 |
12 | static void window_load(Window *window) {
13 | Layer *window_layer = window_get_root_layer(window);
14 | GRect bounds = layer_get_bounds(window_layer);
15 |
16 | s_logo_bitmap = gbitmap_create_with_resource(RESOURCE_ID_LOGO);
17 |
18 | s_logo_layer = bitmap_layer_create(bounds);
19 | bitmap_layer_set_bitmap(s_logo_layer, s_logo_bitmap);
20 | bitmap_layer_set_compositing_mode(s_logo_layer, GCompOpSet);
21 | layer_add_child(window_layer, bitmap_layer_get_layer(s_logo_layer));
22 | }
23 |
24 | static void window_unload(Window *window) {
25 | bitmap_layer_destroy(s_logo_layer);
26 | gbitmap_destroy(s_logo_bitmap);
27 |
28 | window_destroy(s_window);
29 | s_window = NULL;
30 | }
31 |
32 | void splash_window_push() {
33 | if(!s_window) {
34 | s_window = window_create();
35 | window_set_background_color(s_window, GColorBlack);
36 | window_set_window_handlers(s_window, (WindowHandlers) {
37 | .load = window_load,
38 | .unload = window_unload
39 | });
40 | }
41 | window_stack_push(s_window, true);
42 | }
43 |
44 | void splash_window_remove_from_stack() {
45 | if(s_window) {
46 | window_stack_remove(s_window, true);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/pebble/src/c/windows/splash_window.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "../config.h"
6 |
7 | void splash_window_push();
8 |
9 | void splash_window_remove_from_stack();
10 |
--------------------------------------------------------------------------------
/pebble/src/c/windows/unified_window.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "dialog_window.h"
6 | #include "list_window.h"
7 | #include "wakeup_window.h"
8 |
9 | #include "../types.h"
10 |
11 | #include "../modules/comm.h"
12 | #include "../modules/icons.h"
13 |
14 | #include "../util/animation.h"
15 |
16 | void unified_window_push();
17 |
18 | void unified_window_remove_from_stack();
19 |
20 | void unified_window_reload();
21 |
22 | void unified_window_jump_to(int type);
23 |
24 | void unified_window_set_interaction_enabled(bool b);
25 |
--------------------------------------------------------------------------------
/pebble/src/c/windows/wakeup_window.c:
--------------------------------------------------------------------------------
1 | #include "wakeup_window.h"
2 |
3 | static Window *s_window;
4 | static TextLayer *s_title_layer, *s_details_layer;
5 |
6 | static int s_type;
7 | static int s_state;
8 |
9 | static void timer_handler(void *context) {
10 | window_stack_pop_all(true); // Quit wakeup
11 | }
12 |
13 | char* wakeup_window_get_state_string(int state) {
14 | switch(state) {
15 | case ToggleStateWaiting: return "WAITING...";
16 | case ToggleStateOff: return "OFF";
17 | case ToggleStateOn: return "ON";
18 | case ToggleStateLoud: return "LOUD";
19 | case ToggleStateVibrate: return "VIBRATE";
20 | case ToggleStateSilent: return "SILENT";
21 | case ToggleStateBrightnessManual: return "MANUAL";
22 | case ToggleStateBrightnessAutomatic: return "AUTO";
23 | case ToggleStateLocked: return "NOW LOCKED";
24 | default:
25 | APP_LOG(APP_LOG_LEVEL_ERROR, "Unknown state for wakeup_window_get_state_string: %d", state);
26 | return "UNKNOWN";
27 | }
28 | }
29 |
30 | static void window_load(Window *window) {
31 | Layer *window_layer = window_get_root_layer(window);
32 | GRect bounds = layer_get_bounds(window_layer);
33 |
34 | s_title_layer = text_layer_create(GRect(0, PBL_IF_ROUND_ELSE(20, 0), bounds.size.w, 60));
35 | text_layer_set_font(s_title_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD));
36 | text_layer_set_text_alignment(s_title_layer, GTextAlignmentCenter);
37 | text_layer_set_text_color(s_title_layer, GColorWhite);
38 | text_layer_set_background_color(s_title_layer, GColorClear);
39 | text_layer_set_text(s_title_layer, "Dashboard Event");
40 | layer_add_child(window_layer, text_layer_get_layer(s_title_layer));
41 |
42 | s_details_layer = text_layer_create(GRect(0, PBL_IF_ROUND_ELSE(80, 70), bounds.size.w, bounds.size.h));
43 | text_layer_set_font(s_details_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD));
44 | text_layer_set_text_color(s_details_layer, GColorWhite);
45 | text_layer_set_background_color(s_details_layer, GColorClear);
46 | text_layer_set_text_alignment(s_details_layer, GTextAlignmentCenter);
47 | layer_add_child(window_layer, text_layer_get_layer(s_details_layer));
48 |
49 | static char s_buffer[128];
50 | snprintf(s_buffer, sizeof(s_buffer), "Attempting %s to %s\n(Back to dismiss)",
51 | list_window_get_type_string (s_type), wakeup_window_get_state_string(s_state));
52 | text_layer_set_text(s_details_layer, s_buffer);
53 |
54 | // Two minutes
55 | const int two_mins_ms = 120000;
56 | app_timer_register(two_mins_ms, timer_handler, NULL);
57 | }
58 |
59 | static void window_unload(Window *window) {
60 | text_layer_destroy(s_details_layer);
61 |
62 | // Finally
63 | window_destroy(window);
64 | s_window = NULL;
65 | window_stack_pop_all(true);
66 | }
67 |
68 | void wakeup_window_push(int type, int state) {
69 | s_type = type;
70 | s_state = state;
71 |
72 | if(!s_window) {
73 | s_window = window_create();
74 | window_set_background_color(s_window, GColorBlack);
75 | window_set_window_handlers(s_window, (WindowHandlers) {
76 | .load = window_load,
77 | .unload = window_unload
78 | });
79 | }
80 | window_stack_push(s_window, true);
81 | }
82 |
--------------------------------------------------------------------------------
/pebble/src/c/windows/wakeup_window.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "schedule_window.h"
4 |
5 | #include "../types.h"
6 | #include "../modules/comm.h"
7 |
8 | char* wakeup_window_get_state_string(int state); // TODO decouple and move to types.h
9 |
10 | void wakeup_window_push(int type, int state);
11 |
--------------------------------------------------------------------------------
/pebble/wscript:
--------------------------------------------------------------------------------
1 | #
2 | # This file is the default set of rules to compile a Pebble application.
3 | #
4 | # Feel free to customize this to your needs.
5 | #
6 | import os.path
7 |
8 | top = '.'
9 | out = 'build'
10 |
11 |
12 | def options(ctx):
13 | ctx.load('pebble_sdk')
14 |
15 |
16 | def configure(ctx):
17 | """
18 | This method is used to configure your build. ctx.load(`pebble_sdk`) automatically configures
19 | a build for each valid platform in `targetPlatforms`. Platform-specific configuration: add your
20 | change after calling ctx.load('pebble_sdk') and make sure to set the correct environment first.
21 | Universal configuration: add your change prior to calling ctx.load('pebble_sdk').
22 | """
23 | ctx.load('pebble_sdk')
24 |
25 |
26 | def build(ctx):
27 | ctx.load('pebble_sdk')
28 |
29 | build_worker = os.path.exists('worker_src')
30 | binaries = []
31 |
32 | cached_env = ctx.env
33 | for platform in ctx.env.TARGET_PLATFORMS:
34 | ctx.env = ctx.all_envs[platform]
35 | ctx.set_group(ctx.env.PLATFORM_NAME)
36 | app_elf = '{}/pebble-app.elf'.format(ctx.env.BUILD_DIR)
37 | ctx.pbl_build(source=ctx.path.ant_glob('src/c/**/*.c'), target=app_elf, bin_type='app')
38 |
39 | if build_worker:
40 | worker_elf = '{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR)
41 | binaries.append({'platform': platform, 'app_elf': app_elf, 'worker_elf': worker_elf})
42 | ctx.pbl_build(source=ctx.path.ant_glob('worker_src/c/**/*.c'),
43 | target=worker_elf,
44 | bin_type='worker')
45 | else:
46 | binaries.append({'platform': platform, 'app_elf': app_elf})
47 | ctx.env = cached_env
48 |
49 | ctx.set_group('bundle')
50 | ctx.pbl_bundle(binaries=binaries,
51 | js=ctx.path.ant_glob(['src/pkjs/**/*.js',
52 | 'src/pkjs/**/*.json',
53 | 'src/common/**/*.js']),
54 | js_entry_file='src/pkjs/index.js')
55 |
--------------------------------------------------------------------------------