10 |
--------------------------------------------------------------------------------
/res/xml-v12/widget_info.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
--------------------------------------------------------------------------------
/res/layout/history_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-19
15 |
--------------------------------------------------------------------------------
/res/menu/main_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
20 |
--------------------------------------------------------------------------------
/res/layout/widget.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
20 |
21 |
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Wake On Lan
2 | ===========
3 |
4 | A simple Wake On Lan application. Stores history of sent packets to save you remembering MAC addresses.
5 |
6 | You will need to configure your computer to enable Wake On Lan first.
7 |
8 |
9 | ### Wake On Lan Help
10 |
11 | Please refer to the following links to help you get started with Wake On Lan.
12 |
13 |
14 | **A mini-tutorial for Windows/Debian:**
15 |
16 | [`http://android.mafro.net/wol_tutorial.htm`](http://android.mafro.net/wol_tutorial.htm)
17 |
18 |
19 | **One of the original resources for Wake On Lan:**
20 |
21 | [`http://www.depicus.com/wake-on-lan/what-is-wake-on-lan.aspx`](http://www.depicus.com/wake-on-lan/what-is-wake-on-lan.aspx)
22 |
23 |
24 | **The wiki article on Wake On Lan (for the technically minded):**
25 |
26 | [`http://en.wikipedia.org/wiki/Wake_on_lan`](http://en.wikipedia.org/wiki/Wake_on_lan)
27 |
28 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Wake On Lan
4 | A simple Wake On Lan application. Stores history of sent packets to save remembering MAC addresses.
5 | Send Wake Packet
6 | Save & End Editing
7 | Clear Form
8 | Cancel Edit
9 | History
10 | Wake
11 | History
12 | Wake
13 | Reference
14 | MAC Address
15 | Host Name / IP Address
16 | Port
17 | Send Wake Packet
18 | Edit
19 | Remove
20 | Sort By
21 | Recent
22 | Frequent
23 | Created
24 | Invalid MAC Address
25 | Sending Failed
26 | Magic Packet sent
27 |
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (C) 2008-2012 Matt Black.
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 | * Redistributions in binary form must reproduce the above copyright notice,
10 | this list of conditions and the following disclaimer in the documentation
11 | and/or other materials provided with the distribution.
12 | * Neither the name of the author nor the names of its contributors may be used
13 | to endorse or promote products derived from this software without specific
14 | prior written permission.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
17 |
18 |
22 |
23 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
35 |
36 |
37 |
38 |
39 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/src/net/mafro/android/wakeonlan/HistoryListClickListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2013-2014 Yohan Pereira, Matt Black
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright notice, this
9 | list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 | * Neither the name of the author nor the names of its contributors may be used
14 | to endorse or promote products derived from this software without specific
15 | prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | package net.mafro.android.wakeonlan;
30 |
31 | /**
32 | * @desc HistoryListClickListener defines the interface for an object that
33 | * listens to onClick events on the HistoryList
34 | */
35 | interface HistoryListClickListener {
36 |
37 | void onClick(HistoryItem item);
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/CHANGELOG:
--------------------------------------------------------------------------------
1 | v 1.5
2 | introduced new icons and support for several screen densities
3 | font size adjustments and list height across different android resolutions
4 | tablet support
5 | convert mixed case MAC addresses to lower
6 | auto-insert colons into MAC address fixes #6 (thanks to John Gorena)
7 | always trim whitespace on form fixes #7 (thanks to Wei Zhang)
8 | fixed bad ref in StarButton
9 | refactored app, added comments & general cleanup
10 | completely removed custom update-checker; superseded by the Android market
11 |
12 | v 1.4.2
13 | fixed bug in MagicPacket where always used port 9..
14 | fixed exception when parsing non-integer port number
15 |
16 | v 1.4
17 |
18 | implemented settings activity to disable update check. added option to main menu
19 | implemented sorting for history list, with three options: frequency of use, creation date, most recently used
20 | added a Google style star button to each list item which pushes it to the top of the list
21 | fixed bug in HistoryProvider setting invalid default values on an insert
22 | fixed crash when port not set
23 |
24 | v 1.3
25 |
26 | history items now unique on MAC, IP and port
27 | vastly improved the editing process
28 | persist form data until saved/sent
29 | added cancel button for edit mode, clear button for send mode
30 | added inline MAC address validation
31 | display item title in sent notification
32 | clear errors when resetting form
33 | improved MAC address validation via regexp
34 |
35 | v 1.2
36 |
37 | change display text to Host Name / IP Address
38 | updated project for SDK 1.5r1
39 | changed history item padding for android 1.5
40 | changed Delete for Remove, minor debug change in Updater
41 | single click for send packet
42 | further implemented edit, now removes old item
43 |
44 | v 1.1.1
45 |
46 | fixed layout bug, released 1.1.1
47 | fixed bug in Updater when GET fails
48 |
49 | v 1.1.0
50 |
51 | implemented edit
52 | accepts MACs without separators
53 | smart mac address formatting
54 | improved version checking
55 | check for updates once a week
56 |
57 | v 1.0.1
58 |
59 | added ScrollView for wake form
60 |
61 |
--------------------------------------------------------------------------------
/src/net/mafro/android/wakeonlan/HistoryItem.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2008-2012 Matt Black.
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright notice, this
9 | list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 | * Neither the name of the author nor the names of its contributors may be used
14 | to endorse or promote products derived from this software without specific
15 | prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | package net.mafro.android.wakeonlan;
30 |
31 | import android.database.Cursor;
32 |
33 |
34 | /**
35 | * @desc Convenience class for passing history entries around the application
36 | */
37 | public class HistoryItem
38 | {
39 | public int id;
40 | public String title;
41 | public String mac;
42 | public String ip;
43 | public int port;
44 |
45 | public HistoryItem(int id, String title, String mac, String ip, int port)
46 | {
47 | this.id = id;
48 | this.title = title;
49 | this.mac = mac;
50 | this.ip = ip;
51 | this.port = port;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/res/layout/history_row.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
17 |
18 |
23 |
24 |
31 |
32 |
38 |
39 |
45 |
53 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/src/net/mafro/android/wakeonlan/History.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2008-2012 Matt Black.
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright notice, this
9 | list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 | * Neither the name of the author nor the names of its contributors may be used
14 | to endorse or promote products derived from this software without specific
15 | prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | package net.mafro.android.wakeonlan;
30 |
31 | import android.net.Uri;
32 | import android.provider.BaseColumns;
33 |
34 |
35 | /**
36 | * @desc convenience definitions for HistoryProvider
37 | */
38 | public final class History {
39 | public static final String AUTHORITY = "net.mafro.android.wakeonlan.historyprovider";
40 |
41 | private History() {}
42 |
43 | public static final class Items implements BaseColumns {
44 | private Items() {}
45 |
46 | public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/history");
47 |
48 | public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.mafro.wakeonlan.history";
49 | public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.mafro.wakeonlan.history";
50 |
51 | public static final String DEFAULT_SORT_ORDER = "last_used DESC";
52 |
53 | public static final String TITLE = "title";
54 | public static final String MAC = "mac";
55 | public static final String IP = "ip";
56 | public static final String PORT = "port";
57 | public static final String CREATED_DATE = "created";
58 | public static final String LAST_USED_DATE = "last_used";
59 | public static final String USED_COUNT = "used_count";
60 | public static final String IS_STARRED = "is_starred";
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
16 |
20 |
21 |
22 |
23 |
27 |
28 |
32 |
33 |
37 |
41 |
42 |
46 |
50 |
51 |
55 |
59 |
60 |
64 |
68 |
69 |
74 |
75 |
81 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/src/net/mafro/android/widget/StarButton.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2008-2014 Matt Black
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright notice, this
9 | list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 | * Neither the name of the author nor the names of its contributors may be used
14 | to endorse or promote products derived from this software without specific
15 | prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | package net.mafro.android.widget;
30 |
31 | import android.os.Bundle;
32 |
33 | import android.content.Context;
34 |
35 | import android.widget.CompoundButton;
36 | import android.widget.CompoundButton.OnCheckedChangeListener;
37 |
38 | import android.graphics.Canvas;
39 |
40 | import android.util.AttributeSet;
41 | import android.util.Log;
42 |
43 | import net.mafro.android.wakeonlan.R;
44 |
45 |
46 | /**
47 | * @desc Custom button type to implement Google-style favourite star
48 | */
49 | public class StarButton extends CompoundButton implements OnCheckedChangeListener
50 | {
51 |
52 | private static final String TAG = "StarButton";
53 |
54 | public boolean noRender = false;
55 |
56 |
57 | public StarButton(Context context)
58 | {
59 | super(context);
60 | init(context);
61 | }
62 |
63 | public StarButton(Context context, AttributeSet attrs)
64 | {
65 | super(context, attrs);
66 | init(context);
67 | }
68 |
69 | public StarButton(Context context, AttributeSet attrs, int defStyle) {
70 | super(context, attrs, defStyle);
71 | init(context);
72 | }
73 |
74 |
75 | private void init(Context context)
76 | {
77 | setOnCheckedChangeListener(this);
78 | render();
79 | }
80 |
81 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
82 | {
83 | render();
84 | }
85 |
86 | public void render()
87 | {
88 | // render the icon on this button
89 | if(noRender == true) {
90 | setButtonDrawable(android.R.color.transparent);
91 | }else if(isChecked() == true) {
92 | setButtonDrawable(R.drawable.btn_star_big_on);
93 | }else{
94 | setButtonDrawable(R.drawable.btn_star_big_off);
95 | }
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/res/layout-xlarge-mdpi/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
18 |
19 |
25 |
26 |
31 |
32 |
33 |
34 |
40 |
41 |
45 |
46 |
52 |
53 |
58 |
62 |
63 |
68 |
72 |
73 |
78 |
82 |
83 |
88 |
92 |
93 |
98 |
99 |
105 |
112 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/src/net/mafro/android/wakeonlan/WidgetConfigure.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2013-2014 Yohan Pereira, Matt Black
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright notice, this
9 | list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 | * Neither the name of the author nor the names of its contributors may be used
14 | to endorse or promote products derived from this software without specific
15 | prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | package net.mafro.android.wakeonlan;
30 |
31 | import android.app.Activity;
32 | import android.app.PendingIntent;
33 |
34 | import android.appwidget.AppWidgetManager;
35 |
36 | import android.os.Bundle;
37 |
38 | import android.view.View;
39 | import android.content.Intent;
40 | import android.content.SharedPreferences;
41 | import android.content.Context;
42 |
43 | import android.widget.EditText;
44 | import android.widget.ListView;
45 | import android.widget.RemoteViews;
46 |
47 | /**
48 | * @desc This class is used to configure the home screen widget
49 | */
50 | public class WidgetConfigure extends Activity
51 | {
52 |
53 | public static final String TAG = "WidgetConfigure";
54 |
55 | private HistoryListHandler historyListHandler;
56 | private int widget_id;
57 | private SharedPreferences settings;
58 |
59 |
60 | @Override
61 | public void onCreate(Bundle savedInstanceState)
62 | {
63 | super.onCreate(savedInstanceState);
64 |
65 | // Set the result to CANCELED. This will cause the widget host to cancel
66 | // out of the widget placement if they press the back button.
67 | setResult(RESULT_CANCELED);
68 |
69 | setContentView(R.layout.widget_configure);
70 | ListView lv = (ListView)findViewById(R.id.history);
71 | historyListHandler = new HistoryListHandler(this, lv);
72 |
73 | settings = getSharedPreferences(WakeOnLanActivity.TAG, 0);
74 | int sort_mode = settings.getInt("sort_mode", WakeOnLanActivity.CREATED);
75 | historyListHandler.bind(sort_mode);
76 |
77 | // add on click listener
78 | historyListHandler.addHistoryListClickListener(new HistoryListClickListener () {
79 | public void onClick(HistoryItem item) {
80 | selected(item);
81 | }
82 | });
83 |
84 | // get the widget id
85 | Intent intent = getIntent();
86 | widget_id = WidgetProvider.getWidgetId(intent);
87 |
88 | if(widget_id == AppWidgetManager.INVALID_APPWIDGET_ID) {
89 | // no valid widget id; bailing
90 | finish();
91 | }
92 | }
93 |
94 | private void selected(HistoryItem item)
95 | {
96 | // save selected item id to the settings.
97 | WidgetProvider.saveItemPref(settings, item, widget_id);
98 |
99 | // configure the widget
100 | WidgetProvider.configureWidget(widget_id, item, this);
101 |
102 | Intent resultValue = new Intent();
103 | resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widget_id);
104 | setResult(RESULT_OK, resultValue);
105 | finish();
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/src/net/mafro/android/wakeonlan/HistoryAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2008-2014 Matt Black
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright notice, this
9 | list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 | * Neither the name of the author nor the names of its contributors may be used
14 | to endorse or promote products derived from this software without specific
15 | prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | package net.mafro.android.wakeonlan;
30 |
31 | import android.os.Bundle;
32 |
33 | import android.content.Context;
34 | import android.content.ContentResolver;
35 |
36 | import android.database.Cursor;
37 |
38 | import android.content.ContentValues;
39 |
40 | import android.net.Uri;
41 |
42 | import android.util.Log;
43 |
44 | import android.view.View;
45 | import android.view.ViewGroup;
46 | import android.view.ViewParent;
47 |
48 | import android.widget.TextView;
49 | import android.widget.AdapterView;
50 | import android.widget.ResourceCursorAdapter;
51 | import android.widget.CompoundButton;
52 | import android.widget.CompoundButton.OnCheckedChangeListener;
53 |
54 | import net.mafro.android.widget.StarButton;
55 |
56 |
57 | /**
58 | * @desc Custom adapter to aid in UI binding
59 | */
60 | public class HistoryAdapter extends ResourceCursorAdapter implements OnCheckedChangeListener
61 | {
62 |
63 | private static final String TAG = "HistoryAdapter";
64 |
65 | private Context context;
66 | private ContentResolver content;
67 |
68 | boolean showStars;
69 |
70 |
71 | public HistoryAdapter(Context context, Cursor cursor, boolean showStars)
72 | {
73 | super(context, R.layout.history_row, cursor);
74 | this.context = context;
75 | this.content = context.getContentResolver();
76 | this.showStars = showStars;
77 | }
78 |
79 |
80 | @Override
81 | public void bindView(View view, Context context, Cursor cursor)
82 | {
83 | // load our column indexes
84 | int idColumn = cursor.getColumnIndex(History.Items._ID);
85 | int titleColumn = cursor.getColumnIndex(History.Items.TITLE);
86 | int macColumn = cursor.getColumnIndex(History.Items.MAC);
87 | int ipColumn = cursor.getColumnIndex(History.Items.IP);
88 | int portColumn = cursor.getColumnIndex(History.Items.PORT);
89 | int isStarredColumn = cursor.getColumnIndex(History.Items.IS_STARRED);
90 |
91 | TextView vtitle = (TextView) view.findViewById(R.id.history_row_title);
92 | TextView vmac = (TextView) view.findViewById(R.id.history_row_mac);
93 | TextView vip = (TextView) view.findViewById(R.id.history_row_ip);
94 | TextView vport = (TextView) view.findViewById(R.id.history_row_port);
95 | StarButton star = (StarButton) view.findViewById(R.id.history_row_star);
96 |
97 | // bind the cursor data to the form items
98 | vtitle.setText(cursor.getString(titleColumn));
99 | vmac.setText(cursor.getString(macColumn));
100 | vip.setText(cursor.getString(ipColumn));
101 | vport.setText(Integer.toString(cursor.getInt(portColumn)));
102 |
103 | if(this.showStars == true) {
104 | // remove click handler to prevent recursive calls
105 | star.setOnCheckedChangeListener(null);
106 |
107 | // change the star state if different
108 | boolean starred = (cursor.getInt(isStarredColumn) != 0); // non-zero == true
109 | star.setChecked(starred);
110 | star.render();
111 |
112 | // add event listener to star button
113 | star.setOnCheckedChangeListener(this);
114 |
115 | // save our record _ID in the star's tag
116 | star.setTag(cursor.getInt(idColumn));
117 |
118 | }else{
119 | // disable the star button
120 | star.setClickable(false);
121 | star.noRender = true;
122 | star.render();
123 | }
124 | }
125 |
126 |
127 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
128 | {
129 | // extract record's _ID from tag
130 | int id = ((Integer) ((StarButton) buttonView).getTag()).intValue();
131 |
132 | if(isChecked) {
133 | setIsStarred(id, 1);
134 | }else{
135 | setIsStarred(id, 0);
136 | }
137 | }
138 |
139 | private void setIsStarred(int id, int value) {
140 | // update history setting is_starred to value
141 | ContentValues values = new ContentValues(1);
142 | values.put(History.Items.IS_STARRED, value);
143 |
144 | Uri itemUri = Uri.withAppendedPath(History.Items.CONTENT_URI, Integer.toString(id));
145 | this.content.update(itemUri, values, null, null);
146 | }
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/src/net/mafro/android/wakeonlan/MagicPacket.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2008-2014 Matt Black
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright notice, this
9 | list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 | * Neither the name of the author nor the names of its contributors may be used
14 | to endorse or promote products derived from this software without specific
15 | prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | package net.mafro.android.wakeonlan;
30 |
31 | import android.util.Log;
32 |
33 | import java.io.IOException;
34 |
35 | import java.net.InetAddress;
36 | import java.net.UnknownHostException;
37 | import java.net.DatagramSocket;
38 | import java.net.DatagramPacket;
39 | import java.net.SocketException;
40 |
41 | import java.lang.IllegalArgumentException;
42 | import java.lang.StringBuffer;
43 |
44 | import java.util.regex.Pattern;
45 | import java.util.regex.Matcher;
46 |
47 |
48 | /**
49 | * @desc Static WOL magic packet class
50 | */
51 | public class MagicPacket
52 | {
53 | private static final String TAG = "MagicPacket";
54 |
55 | public static final String BROADCAST = "192.168.1.255";
56 | public static final int PORT = 9;
57 | public static final char SEPARATOR = ':';
58 |
59 | public static String send(String mac, String ip) throws UnknownHostException, SocketException, IOException, IllegalArgumentException
60 | {
61 | return send(mac, ip, PORT);
62 | }
63 |
64 | public static String send(String mac, String ip, int port) throws UnknownHostException, SocketException, IOException, IllegalArgumentException
65 | {
66 | // validate MAC and chop into array
67 | final String[] hex = validateMac(mac);
68 |
69 | // convert to base16 bytes
70 | final byte[] macBytes = new byte[6];
71 | for(int i=0; i<6; i++) {
72 | macBytes[i] = (byte) Integer.parseInt(hex[i], 16);
73 | }
74 |
75 | final byte[] bytes = new byte[102];
76 |
77 | // fill first 6 bytes
78 | for(int i=0; i<6; i++) {
79 | bytes[i] = (byte) 0xff;
80 | }
81 | // fill remaining bytes with target MAC
82 | for(int i=6; i 1) && (i % 2 == 0)) {
139 | newMac += ":";
140 | }
141 | newMac += mac.charAt(i);
142 | }
143 | }else{
144 | newMac = mac;
145 | }
146 |
147 | // regexp pattern match a valid MAC address
148 | final Pattern pat = Pattern.compile("((([0-9a-fA-F]){2}[-:]){5}([0-9a-fA-F]){2})");
149 | final Matcher m = pat.matcher(newMac);
150 |
151 | if(m.find()) {
152 | String result = m.group();
153 | return result.split("(\\:|\\-)");
154 | }else{
155 | throw new IllegalArgumentException("Invalid MAC address");
156 | }
157 | }
158 |
159 | public static void main(String[] args) {
160 | if(args.length != 2) {
161 | System.out.println("Usage: java MagicPacket ");
162 | System.out.println("Example: java MagicPacket 192.168.0.255 00:0D:61:08:22:4A");
163 | System.out.println("Example: java MagicPacket 192.168.0.255 00-0D-61-08-22-4A");
164 | System.exit(1);
165 | }
166 |
167 | String ipStr = args[0];
168 | String macStr = args[1];
169 |
170 | try {
171 | macStr = MagicPacket.cleanMac(macStr);
172 | System.out.println("Sending to: "+macStr);
173 | MagicPacket.send(macStr, ipStr);
174 | }
175 | catch(IllegalArgumentException e) {
176 | System.out.println(e.getMessage());
177 | }catch(Exception e) {
178 | System.out.println("Failed to send Wake-on-LAN packet:" + e.getMessage());
179 | }
180 | }
181 |
182 | }
183 |
--------------------------------------------------------------------------------
/src/net/mafro/android/wakeonlan/HistoryListHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2008-2014 Matt Black
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright notice, this
9 | list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 | * Neither the name of the author nor the names of its contributors may be used
14 | to endorse or promote products derived from this software without specific
15 | prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | package net.mafro.android.wakeonlan;
30 |
31 | import android.app.Activity;
32 |
33 | import android.content.ContentValues;
34 |
35 | import android.database.Cursor;
36 |
37 | import android.util.Log;
38 |
39 | import android.widget.ListView;
40 | import android.widget.AdapterView;
41 | import android.widget.AdapterView.OnItemClickListener;
42 |
43 | import android.view.View;
44 |
45 | import android.net.Uri;
46 |
47 | import java.util.List;
48 | import java.util.ArrayList;
49 |
50 |
51 | /**
52 | * @desc Class handles all functions of the history ListView
53 | */
54 | public class HistoryListHandler implements OnItemClickListener
55 | {
56 |
57 | public static final String TAG = "HistoryListHandler";
58 |
59 | private Activity parent;
60 | private Cursor cursor;
61 | private HistoryAdapter adapter;
62 | private List listeners;
63 |
64 |
65 | public static final String[] PROJECTION = new String[]
66 | {
67 | History.Items._ID,
68 | History.Items.TITLE,
69 | History.Items.MAC,
70 | History.Items.IP,
71 | History.Items.PORT,
72 | History.Items.LAST_USED_DATE,
73 | History.Items.USED_COUNT,
74 | History.Items.IS_STARRED
75 | };
76 |
77 | private ListView view = null;
78 |
79 |
80 | public HistoryListHandler(Activity parent, ListView view)
81 | {
82 | this.parent = parent;
83 | this.view = view;
84 | this.listeners = new ArrayList();
85 | }
86 |
87 | public void bind(int sort_mode)
88 | {
89 | String orderBy = null;
90 | switch (sort_mode) {
91 | case WakeOnLanActivity.CREATED:
92 | orderBy = History.Items.IS_STARRED+" DESC, "+History.Items.CREATED_DATE+" DESC";
93 | break;
94 | case WakeOnLanActivity.LAST_USED:
95 | orderBy = History.Items.IS_STARRED+" DESC, "+History.Items.LAST_USED_DATE+" DESC";
96 | break;
97 | case WakeOnLanActivity.USED_COUNT:
98 | orderBy = History.Items.IS_STARRED+" DESC, "+History.Items.USED_COUNT+" DESC";
99 | break;
100 | }
101 |
102 | // determine if we render the favourite star buttons
103 | boolean showStars = false;
104 | if(parent instanceof WakeOnLanActivity) {
105 | showStars = true;
106 | }
107 |
108 | // load History cursor via custom ResourceAdapter
109 | cursor = parent.getContentResolver().query(History.Items.CONTENT_URI, PROJECTION, null, null, orderBy);
110 | adapter = new HistoryAdapter(parent, cursor, showStars);
111 |
112 | // register self as listener for item clicks
113 | view.setOnItemClickListener(this);
114 |
115 | // bind to the supplied view
116 | view.setAdapter(adapter);
117 | }
118 |
119 |
120 | public void onItemClick(AdapterView av, View v, int position, long id)
121 | {
122 | if(position >= 0) {
123 | // extract item at position of click
124 | HistoryItem item = getItem(position);
125 |
126 | // fire onClick event to HistoryListListeners
127 | for(HistoryListClickListener l : listeners) {
128 | l.onClick(item);
129 | }
130 | }
131 | }
132 |
133 | public HistoryItem getItem(int position)
134 | {
135 | this.cursor.moveToPosition(position);
136 | return getItem(this.cursor);
137 | }
138 |
139 | public static HistoryItem getItem(Cursor cursor)
140 | {
141 | int idColumn = cursor.getColumnIndex(History.Items._ID);
142 | int titleColumn = cursor.getColumnIndex(History.Items.TITLE);
143 | int macColumn = cursor.getColumnIndex(History.Items.MAC);
144 | int ipColumn = cursor.getColumnIndex(History.Items.IP);
145 | int portColumn = cursor.getColumnIndex(History.Items.PORT);
146 |
147 | return new HistoryItem(cursor.getInt(idColumn), cursor.getString(titleColumn), cursor.getString(macColumn), cursor.getString(ipColumn), cursor.getInt(portColumn));
148 | }
149 |
150 | public void addToHistory(String title, String mac, String ip, int port)
151 | {
152 | boolean exists = false;
153 |
154 | // don't allow duplicates in history list
155 | if(cursor.moveToFirst()) {
156 | int macColumn = cursor.getColumnIndex(History.Items.MAC);
157 | int ipColumn = cursor.getColumnIndex(History.Items.IP);
158 | int portColumn = cursor.getColumnIndex(History.Items.PORT);
159 |
160 | do {
161 | if(mac.equals(cursor.getString(macColumn)) && ip.equals(cursor.getString(ipColumn)) && (port == cursor.getInt(portColumn))) {
162 | exists = true;
163 | break;
164 | }
165 | } while (cursor.moveToNext());
166 | }
167 |
168 | // create only if the item doesn't exist
169 | if(exists == false) {
170 | ContentValues values = new ContentValues(4);
171 | values.put(History.Items.TITLE, title);
172 | values.put(History.Items.MAC, mac);
173 | values.put(History.Items.IP, ip);
174 | values.put(History.Items.PORT, port);
175 | this.parent.getContentResolver().insert(History.Items.CONTENT_URI, values);
176 | }
177 | }
178 |
179 | public void updateHistory(int id, String title, String mac, String ip, int port)
180 | {
181 | ContentValues values = new ContentValues(4);
182 | values.put(History.Items.TITLE, title);
183 | values.put(History.Items.MAC, mac);
184 | values.put(History.Items.IP, ip);
185 | values.put(History.Items.PORT, port);
186 |
187 | Uri itemUri = Uri.withAppendedPath(History.Items.CONTENT_URI, Integer.toString(id));
188 | this.parent.getContentResolver().update(itemUri, values, null, null);
189 | }
190 |
191 | public void incrementHistory(long id)
192 | {
193 | int usedCountColumn = cursor.getColumnIndex(History.Items.USED_COUNT);
194 | int usedCount = cursor.getInt(usedCountColumn);
195 |
196 | ContentValues values = new ContentValues(1);
197 | values.put(History.Items.USED_COUNT, usedCount+1);
198 | values.put(History.Items.LAST_USED_DATE, Long.valueOf(System.currentTimeMillis()));
199 |
200 | Uri itemUri = Uri.withAppendedPath(History.Items.CONTENT_URI, Long.toString(id));
201 | this.parent.getContentResolver().update(itemUri, values, null, null);
202 | }
203 |
204 | public void deleteHistory(int id)
205 | {
206 | // use HistoryProvider to remove this row
207 | Uri itemUri = Uri.withAppendedPath(History.Items.CONTENT_URI, Integer.toString(id));
208 | this.parent.getContentResolver().delete(itemUri, null, null);
209 | }
210 |
211 | public void addHistoryListClickListener(HistoryListClickListener l)
212 | {
213 | this.listeners.add(l);
214 | }
215 |
216 | public void removeHistoryListClickListener(HistoryListClickListener l)
217 | {
218 | this.listeners.remove(l);
219 | }
220 |
221 | }
222 |
--------------------------------------------------------------------------------
/src/net/mafro/android/wakeonlan/WidgetProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2013-2014 Yohan Pereira, Matt Black
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright notice, this
9 | list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 | * Neither the name of the author nor the names of its contributors may be used
14 | to endorse or promote products derived from this software without specific
15 | prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | package net.mafro.android.wakeonlan;
30 |
31 | import android.app.PendingIntent;
32 |
33 | import android.database.Cursor;
34 |
35 | import android.appwidget.AppWidgetManager;
36 | import android.appwidget.AppWidgetProvider;
37 |
38 | import android.content.Intent;
39 | import android.content.Context;
40 | import android.content.SharedPreferences;
41 |
42 | import android.os.Bundle;
43 |
44 | import android.widget.RemoteViews;
45 | import android.util.Log;
46 |
47 | /**
48 | * @desc This class is used to setup the home screen widget, as well as handle click events
49 | */
50 |
51 | public class WidgetProvider extends AppWidgetProvider
52 | {
53 |
54 | public static final String TAG = "WidgetProvider";
55 |
56 | public static final String SETTINGS_PREFIX = "widget_";
57 | public static final String WIDGET_ONCLICK = "net.mafro.android.wakeonlan.WidgetOnClick";
58 |
59 | /**
60 | * @desc this method is called once when the WidgetHost starts (usually when the OS boots).
61 | */
62 | @Override
63 | public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
64 | {
65 | super.onUpdate(context, appWidgetManager, appWidgetIds);
66 |
67 | SharedPreferences settings = context.getSharedPreferences(WakeOnLanActivity.TAG, 0);
68 |
69 | final int N = appWidgetIds.length;
70 | for(int i=0; i sHistoryProjectionMap;
64 |
65 | private static final String HISTORY_TABLE_NAME = "history";
66 |
67 | private static final int HISTORY = 1;
68 | private static final int HISTORY_ID = 2;
69 |
70 | private static final UriMatcher sUriMatcher;
71 |
72 | /**
73 | * This class helps open, create, and upgrade the database file.
74 | */
75 | private static class DatabaseHelper extends SQLiteOpenHelper {
76 |
77 | public DatabaseHelper(Context context) {
78 | super(context, DATABASE_NAME, null, DATABASE_VERSION);
79 | }
80 |
81 | @Override
82 | public void onCreate(SQLiteDatabase db) {
83 | db.execSQL("CREATE TABLE history ("
84 | + History.Items._ID + " INTEGER PRIMARY KEY,"
85 | + History.Items.TITLE + " TEXT,"
86 | + History.Items.MAC + " TEXT,"
87 | + History.Items.IP + " TEXT,"
88 | + History.Items.PORT + " INTEGER,"
89 | + History.Items.CREATED_DATE + " INTEGER,"
90 | + History.Items.LAST_USED_DATE + " INTEGER,"
91 | + History.Items.USED_COUNT + " INTEGER DEFAULT 1,"
92 | + History.Items.IS_STARRED + " INTEGER DEFAULT 0"
93 | + ");");
94 | }
95 |
96 | @Override
97 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
98 | if((oldVersion == 1) && (newVersion == 2)) {
99 | db.execSQL("ALTER TABLE history ADD COLUMN " + History.Items.USED_COUNT + " INTEGER DEFAULT 1;");
100 | db.execSQL("ALTER TABLE history ADD COLUMN " + History.Items.IS_STARRED + " INTEGER DEFAULT 0;");
101 | }
102 | }
103 | }
104 |
105 | private DatabaseHelper mOpenHelper;
106 |
107 | @Override
108 | public boolean onCreate() {
109 | mOpenHelper = new DatabaseHelper(getContext());
110 | return true;
111 | }
112 |
113 | @Override
114 | public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
115 | SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
116 | qb.setTables(HISTORY_TABLE_NAME);
117 | qb.setProjectionMap(sHistoryProjectionMap);
118 |
119 | // if no sort order is specified use the default
120 | String orderBy;
121 | if(TextUtils.isEmpty(sortOrder)) {
122 | orderBy = History.Items.DEFAULT_SORT_ORDER;
123 | }else{
124 | orderBy = sortOrder;
125 | }
126 |
127 | // get the database and run the query
128 | SQLiteDatabase db = mOpenHelper.getReadableDatabase();
129 | Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
130 |
131 | // tell the cursor what uri to watch, so it knows when its source data changes
132 | c.setNotificationUri(getContext().getContentResolver(), uri);
133 | return c;
134 | }
135 |
136 | @Override
137 | public String getType(Uri uri) {
138 | switch (sUriMatcher.match(uri)) {
139 | case HISTORY:
140 | return History.Items.CONTENT_TYPE;
141 |
142 | case HISTORY_ID:
143 | return History.Items.CONTENT_ITEM_TYPE;
144 |
145 | default:
146 | throw new IllegalArgumentException("Unknown URI " + uri);
147 | }
148 | }
149 |
150 | @Override
151 | public Uri insert(Uri uri, ContentValues initialValues) {
152 | // validate the requested uri
153 | if(sUriMatcher.match(uri) != HISTORY) {
154 | throw new IllegalArgumentException("Unknown URI " + uri);
155 | }
156 |
157 | ContentValues values;
158 | if(initialValues != null) {
159 | values = new ContentValues(initialValues);
160 | } else {
161 | values = new ContentValues();
162 | }
163 |
164 | Long now = Long.valueOf(System.currentTimeMillis());
165 |
166 | // make sure that the fields are all set
167 | if(values.containsKey(History.Items.TITLE) == false) {
168 | values.put(History.Items.TITLE, "");
169 | }
170 | if(values.containsKey(History.Items.MAC) == false) {
171 | values.put(History.Items.MAC, "");
172 | }
173 | if(values.containsKey(History.Items.IP) == false) {
174 | values.put(History.Items.IP, "");
175 | }
176 | if(values.containsKey(History.Items.PORT) == false) {
177 | values.put(History.Items.PORT, "");
178 | }
179 | if(values.containsKey(History.Items.CREATED_DATE) == false) {
180 | values.put(History.Items.CREATED_DATE, now);
181 | }
182 | if(values.containsKey(History.Items.LAST_USED_DATE) == false) {
183 | values.put(History.Items.LAST_USED_DATE, now);
184 | }
185 |
186 | SQLiteDatabase db = mOpenHelper.getWritableDatabase();
187 |
188 | // insert record, 2nd param is NULLABLE field for if values is empty
189 | long rowId = db.insert(HISTORY_TABLE_NAME, History.Items.MAC, values);
190 | if(rowId > 0) {
191 | Uri histUri = ContentUris.withAppendedId(History.Items.CONTENT_URI, rowId);
192 | getContext().getContentResolver().notifyChange(histUri, null);
193 | return histUri;
194 | }
195 |
196 | throw new SQLException("Failed to insert row into " + uri);
197 | }
198 |
199 | @Override
200 | public int delete(Uri uri, String where, String[] whereArgs) {
201 | SQLiteDatabase db = mOpenHelper.getWritableDatabase();
202 | int count;
203 | switch (sUriMatcher.match(uri)) {
204 | case HISTORY:
205 | count = db.delete(HISTORY_TABLE_NAME, where, whereArgs);
206 | break;
207 |
208 | case HISTORY_ID:
209 | String histId = uri.getPathSegments().get(1);
210 | count = db.delete(HISTORY_TABLE_NAME, History.Items._ID + "=" + histId
211 | + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs);
212 | break;
213 |
214 | default:
215 | throw new IllegalArgumentException("Unknown URI " + uri);
216 | }
217 |
218 | getContext().getContentResolver().notifyChange(uri, null);
219 | return count;
220 | }
221 |
222 | @Override
223 | public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
224 | SQLiteDatabase db = mOpenHelper.getWritableDatabase();
225 |
226 | int count;
227 | switch (sUriMatcher.match(uri)) {
228 | case HISTORY:
229 | count = db.update(HISTORY_TABLE_NAME, values, where, whereArgs);
230 | break;
231 |
232 | case HISTORY_ID:
233 | String historyId = uri.getPathSegments().get(1);
234 | count = db.update(HISTORY_TABLE_NAME, values, History.Items._ID + "=" + historyId + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs);
235 | break;
236 |
237 | default:
238 | throw new IllegalArgumentException("Unknown URI " + uri);
239 | }
240 |
241 | getContext().getContentResolver().notifyChange(uri, null);
242 | return count;
243 | }
244 |
245 | static {
246 | sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
247 | sUriMatcher.addURI(History.AUTHORITY, "history", HISTORY);
248 | sUriMatcher.addURI(History.AUTHORITY, "history/#", HISTORY_ID);
249 |
250 | sHistoryProjectionMap = new HashMap();
251 | sHistoryProjectionMap.put(History.Items._ID, History.Items._ID);
252 | sHistoryProjectionMap.put(History.Items.TITLE, History.Items.TITLE);
253 | sHistoryProjectionMap.put(History.Items.MAC, History.Items.MAC);
254 | sHistoryProjectionMap.put(History.Items.IP, History.Items.IP);
255 | sHistoryProjectionMap.put(History.Items.PORT, History.Items.PORT);
256 | sHistoryProjectionMap.put(History.Items.CREATED_DATE, History.Items.CREATED_DATE);
257 | sHistoryProjectionMap.put(History.Items.LAST_USED_DATE, History.Items.LAST_USED_DATE);
258 | sHistoryProjectionMap.put(History.Items.USED_COUNT, History.Items.USED_COUNT);
259 | sHistoryProjectionMap.put(History.Items.IS_STARRED, History.Items.IS_STARRED);
260 | }
261 |
262 | }
263 |
--------------------------------------------------------------------------------
/src/net/mafro/android/wakeonlan/WakeOnLanActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2008-2014 Matt Black
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright notice, this
9 | list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 | * Neither the name of the author nor the names of its contributors may be used
14 | to endorse or promote products derived from this software without specific
15 | prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | package net.mafro.android.wakeonlan;
30 |
31 | import android.app.Activity;
32 | import android.app.LocalActivityManager;
33 |
34 | import android.os.Bundle;
35 |
36 | import android.content.Context;
37 | import android.content.UriMatcher;
38 | import android.content.ContentValues;
39 | import android.content.SharedPreferences;
40 |
41 | import android.util.Log;
42 |
43 | import android.widget.TabHost;
44 | import android.widget.TabHost.OnTabChangeListener;
45 | import android.widget.ListView;
46 | import android.widget.TextView;
47 | import android.widget.EditText;
48 | import android.widget.Button;
49 | import android.widget.Toast;
50 | import android.widget.TextView;
51 | import android.widget.AdapterView;
52 | import android.widget.AdapterView.AdapterContextMenuInfo;
53 |
54 | import android.view.View;
55 | import android.view.View.OnClickListener;
56 | import android.view.View.OnFocusChangeListener;
57 | import android.view.ContextMenu;
58 | import android.view.Menu;
59 | import android.view.MenuItem;
60 | import android.view.ContextMenu.ContextMenuInfo;
61 | import android.view.MenuInflater;
62 |
63 |
64 | /**
65 | * @desc Base activity, handles all UI events except history ListView clicks
66 | */
67 | public class WakeOnLanActivity extends Activity implements OnClickListener, OnTabChangeListener, OnFocusChangeListener
68 | {
69 |
70 | public static final String TAG = "WakeOnLan";
71 |
72 | public static final int MENU_ITEM_WAKE = Menu.FIRST;
73 | public static final int MENU_ITEM_DELETE = Menu.FIRST + 1;
74 |
75 | private static int _editModeID = 0;
76 | private static boolean typingMode = false;
77 |
78 | private static boolean isTablet = false;
79 | private TabHost th;
80 |
81 | private HistoryListHandler histHandler;
82 | private static int sort_mode;
83 |
84 | public static final int CREATED = 0;
85 | public static final int LAST_USED = 1;
86 | public static final int USED_COUNT = 2;
87 |
88 | private static final String[] PROJECTION = new String[]
89 | {
90 | History.Items._ID,
91 | History.Items.TITLE,
92 | History.Items.MAC,
93 | History.Items.IP,
94 | History.Items.PORT,
95 | History.Items.LAST_USED_DATE,
96 | History.Items.USED_COUNT,
97 | History.Items.IS_STARRED
98 | };
99 |
100 | private static Toast notification;
101 |
102 |
103 | @Override
104 | public void onCreate(Bundle savedInstanceState)
105 | {
106 | super.onCreate(savedInstanceState);
107 | setContentView(R.layout.main);
108 |
109 | // configure tabs
110 | th = (TabHost)findViewById(R.id.tabhost);
111 |
112 | // tabs only exist in phone layouts
113 | if(th != null) {
114 | WakeOnLanActivity.isTablet = true;
115 |
116 | LocalActivityManager lam = new LocalActivityManager(this, false);
117 | th.setup(lam);
118 |
119 | th.addTab(th.newTabSpec("tab_history").setIndicator(getString(R.string.tab_history), getResources().getDrawable(R.drawable.ical)).setContent(R.id.historyview));
120 | th.addTab(th.newTabSpec("tab_wake").setIndicator(getString(R.string.tab_wake), getResources().getDrawable(R.drawable.wake)).setContent(R.id.wakeview));
121 |
122 | th.setCurrentTab(0);
123 |
124 | // register self as tab changed listener
125 | th.setOnTabChangedListener(this);
126 | }else{
127 | // set the background colour of the titles
128 | TextView historytitle = (TextView)findViewById(R.id.historytitle);
129 | historytitle.setBackgroundColor(0xFF999999);
130 | TextView waketitle = (TextView)findViewById(R.id.waketitle);
131 | waketitle.setBackgroundColor(0xFF999999);
132 | }
133 |
134 | // set defaults on Wake tab
135 | EditText vip = (EditText)findViewById(R.id.ip);
136 | vip.setText(MagicPacket.BROADCAST);
137 | EditText vport = (EditText)findViewById(R.id.port);
138 | vport.setText(Integer.toString(MagicPacket.PORT));
139 |
140 |
141 | // register self as listener for wake button
142 | Button sendWake = (Button)findViewById(R.id.send_wake);
143 | sendWake.setOnClickListener(this);
144 | Button clearWake = (Button)findViewById(R.id.clear_wake);
145 | clearWake.setOnClickListener(this);
146 |
147 | // register self as mac address field focus change listener
148 | EditText vmac = (EditText)findViewById(R.id.mac);
149 | vmac.setOnFocusChangeListener(this);
150 |
151 |
152 | // preferences
153 | SharedPreferences settings = getSharedPreferences(TAG, 0);
154 | SharedPreferences.Editor editor;
155 |
156 | // clean up old preferences
157 | if(settings.contains("check_for_update") == true) {
158 | editor = settings.edit();
159 | editor.remove("check_for_update");
160 | editor.remove("last_update");
161 | editor.commit();
162 | }
163 |
164 | // load our sort mode
165 | sort_mode = settings.getInt("sort_mode", CREATED);
166 |
167 |
168 | // grab the history ListView
169 | ListView lv = (ListView)findViewById(R.id.history);
170 |
171 | // load history handler (deals with cursor and history ListView)
172 | histHandler = new HistoryListHandler(this, lv);
173 | histHandler.bind(sort_mode);
174 |
175 | // add listener to get on click events
176 | histHandler.addHistoryListClickListener(new HistoryListClickListener() {
177 | public void onClick(HistoryItem item) {
178 | onHistoryItemClick(item);
179 | }
180 | });
181 |
182 | // register main Activity as context menu handler
183 | registerForContextMenu(lv);
184 | }
185 |
186 |
187 | public boolean onCreateOptionsMenu(Menu menu)
188 | {
189 | MenuInflater inflater = getMenuInflater();
190 | inflater.inflate(R.menu.main_menu, menu);
191 |
192 | MenuItem mi = null;
193 |
194 | switch (sort_mode) {
195 | case CREATED:
196 | mi = (MenuItem) menu.findItem(R.id.menu_created);
197 | break;
198 | case LAST_USED:
199 | mi = (MenuItem) menu.findItem(R.id.menu_lastused);
200 | break;
201 | case USED_COUNT:
202 | mi = (MenuItem) menu.findItem(R.id.menu_usedcount);
203 | break;
204 | }
205 |
206 | // toggle menuitem
207 | mi.setChecked(true);
208 | return true;
209 | }
210 |
211 | public boolean onOptionsItemSelected(MenuItem mi)
212 | {
213 | switch (mi.getItemId()) {
214 | case R.id.menu_created:
215 | sort_mode = CREATED;
216 | break;
217 | case R.id.menu_lastused:
218 | sort_mode = LAST_USED;
219 | break;
220 | case R.id.menu_usedcount:
221 | sort_mode = USED_COUNT;
222 | break;
223 | case R.id.menu_sortby:
224 | return false;
225 | }
226 |
227 | // toggle menuitem
228 | mi.setChecked(true);
229 |
230 | // save to preferences
231 | SharedPreferences.Editor editor = getSharedPreferences(TAG, 0).edit();
232 | editor.putInt("sort_mode", sort_mode);
233 | editor.commit();
234 |
235 | // rebind the history list
236 | histHandler.bind(sort_mode);
237 | return true;
238 | }
239 |
240 |
241 | public void onClick(View v)
242 | {
243 | if(v.getId() == R.id.send_wake) {
244 | EditText vtitle = (EditText)findViewById(R.id.title);
245 | EditText vmac = (EditText)findViewById(R.id.mac);
246 | EditText vip = (EditText)findViewById(R.id.ip);
247 | EditText vport = (EditText)findViewById(R.id.port);
248 |
249 | String title = vtitle.getText().toString().trim();
250 | String mac = vmac.getText().toString().trim();
251 |
252 | // default IP and port unless set on form
253 | String ip = MagicPacket.BROADCAST;
254 | if(!vip.getText().toString().trim().equals("")) {
255 | ip = vip.getText().toString().trim();
256 | }
257 |
258 | int port = MagicPacket.PORT;
259 | if(!vport.getText().toString().trim().equals("")) {
260 | try {
261 | port = Integer.valueOf(vport.getText().toString().trim());
262 | }catch(NumberFormatException nfe) {
263 | notifyUser("Bad port number", WakeOnLanActivity.this);
264 | return;
265 | }
266 | }
267 |
268 | // update form with cleaned variables
269 | vtitle.setText(title);
270 | vmac.setText(mac);
271 | vip.setText(ip);
272 | vport.setText(Integer.toString(port));
273 |
274 | // check for edit mode - no send of packet
275 | if(_editModeID == 0) {
276 | // send the magic packet
277 | String formattedMac = sendPacket(WakeOnLanActivity.this, title, mac, ip, port);
278 |
279 | // on successful send, add to history list
280 | if(formattedMac != null) {
281 | histHandler.addToHistory(title, formattedMac, ip, port);
282 | }else{
283 | // return on sending failed
284 | return;
285 | }
286 |
287 | }else{
288 | String formattedMac = null;
289 |
290 | try {
291 | // validate and clean our mac address
292 | formattedMac = MagicPacket.cleanMac(mac);
293 |
294 | }catch(IllegalArgumentException iae) {
295 | notifyUser(iae.getMessage(), WakeOnLanActivity.this);
296 | return;
297 | }
298 |
299 | // update existing history entry
300 | histHandler.updateHistory(_editModeID, title, formattedMac, ip, port);
301 |
302 | // reset now edit mode complete
303 | _editModeID = 0;
304 | }
305 |
306 | // finished typing (either send or edit)
307 | typingMode = false;
308 |
309 | // switch back to the history tab
310 | if(WakeOnLanActivity.isTablet == true) {
311 | th.setCurrentTab(0);
312 | }
313 |
314 | }else if(v.getId() == R.id.clear_wake) {
315 | if(_editModeID == 0) {
316 | // clear the form
317 | EditText vtitle = (EditText)findViewById(R.id.title);
318 | vtitle.setText(null);
319 | EditText vmac = (EditText)findViewById(R.id.mac);
320 | vmac.setText(null);
321 | vmac.setError(null);
322 | EditText vip = (EditText)findViewById(R.id.ip);
323 | vip.setText(null);
324 | EditText vport = (EditText)findViewById(R.id.port);
325 | vport.setText(null);
326 | }else{
327 | // cancel editing
328 | _editModeID = 0;
329 | typingMode = false;
330 |
331 | // switch back to the history tab
332 | if(WakeOnLanActivity.isTablet == true) {
333 | th.setCurrentTab(0);
334 | }
335 | }
336 | }
337 | }
338 |
339 | public void onTabChanged(String tabId)
340 | {
341 | if(tabId.equals("tab_wake")) {
342 | // enter typing mode - no clear of form until exit typing mode
343 | typingMode = true;
344 |
345 | }else if(tabId.equals("tab_history")) {
346 | // set form back to defaults, if typing mode has ended (button was clicked)
347 | if(typingMode == false) {
348 | EditText vtitle = (EditText)findViewById(R.id.title);
349 | EditText vmac = (EditText)findViewById(R.id.mac);
350 | EditText vip = (EditText)findViewById(R.id.ip);
351 | EditText vport = (EditText)findViewById(R.id.port);
352 |
353 | vtitle.setText(null);
354 | vmac.setText(null);
355 | vip.setText(MagicPacket.BROADCAST);
356 | vport.setText(Integer.toString(MagicPacket.PORT));
357 |
358 | // clear any errors
359 | vmac.setError(null);
360 |
361 | // reset both our button's text
362 | Button sendWake = (Button)findViewById(R.id.send_wake);
363 | sendWake.setText(R.string.button_wake);
364 | Button clearWake = (Button)findViewById(R.id.clear_wake);
365 | clearWake.setText(R.string.button_clear);
366 | }
367 | }
368 | }
369 |
370 | public void onFocusChange(View v, boolean hasFocus)
371 | {
372 | // validate mac address on field exit
373 | if(hasFocus == false) {
374 | EditText vmac = (EditText)v;
375 |
376 | try {
377 | // validate our mac address
378 | String mac = vmac.getText().toString();
379 | if(mac.length() > 0) {
380 | mac = MagicPacket.cleanMac(mac);
381 | vmac.setText(mac);
382 | }
383 | vmac.setError(null);
384 |
385 | }catch(IllegalArgumentException iae) {
386 | vmac.setError(getString(R.string.invalid_mac));
387 | }
388 | }
389 | }
390 |
391 |
392 | public String sendPacket(HistoryItem item)
393 | {
394 | return sendPacket(WakeOnLanActivity.this, item.title, item.mac, item.ip, item.port);
395 | }
396 |
397 | public static String sendPacket(Context context,String title, String mac, String ip, int port)
398 | {
399 | String formattedMac = null;
400 |
401 | try {
402 | formattedMac = MagicPacket.send(mac, ip, port);
403 |
404 | }catch(IllegalArgumentException iae) {
405 | notifyUser(context.getString(R.string.send_failed)+":\n"+iae.getMessage(), context);
406 | return null;
407 |
408 | }catch(Exception e) {
409 | notifyUser(context.getString(R.string.send_failed), context);
410 | return null;
411 | }
412 |
413 | // display sent message to user
414 | notifyUser(context.getString(R.string.packet_sent)+" to "+title, context);
415 | return formattedMac;
416 | }
417 |
418 |
419 | @Override
420 | public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
421 | {
422 | super.onCreateContextMenu(menu, v, menuInfo);
423 | MenuInflater inflater = getMenuInflater();
424 | inflater.inflate(R.menu.history_menu, menu);
425 | }
426 |
427 | @Override
428 | public boolean onContextItemSelected(MenuItem mi)
429 | {
430 | // extract data about clicked item
431 | AdapterContextMenuInfo info = (AdapterContextMenuInfo) mi.getMenuInfo();
432 |
433 | // extract history item
434 | HistoryItem item = histHandler.getItem(info.position);
435 |
436 | switch (mi.getItemId()) {
437 | case R.id.menu_wake:
438 | String mac = sendPacket(item);
439 |
440 | // update used count in DB
441 | if(sendPacket(item) != null) {
442 | histHandler.incrementHistory(item.id);
443 | }
444 | return true;
445 |
446 | case R.id.menu_edit:
447 | // save the id of record being edited
448 | _editModeID = item.id;
449 |
450 | // fire this record into edit mode in the next tab
451 | EditText vtitle = (EditText)findViewById(R.id.title);
452 | EditText vmac = (EditText)findViewById(R.id.mac);
453 | EditText vip = (EditText)findViewById(R.id.ip);
454 | EditText vport = (EditText)findViewById(R.id.port);
455 |
456 | // display editing data
457 | vtitle.setText(item.title);
458 | vmac.setText(item.mac);
459 | vip.setText(item.ip);
460 | vport.setText(Integer.toString(item.port));
461 |
462 | // clear any previous errors
463 | vmac.setError(null);
464 |
465 | // change text on both our buttons
466 | Button saveEdit = (Button)findViewById(R.id.send_wake);
467 | saveEdit.setText(R.string.button_save);
468 | Button cancelEdit = (Button)findViewById(R.id.clear_wake);
469 | cancelEdit.setText(R.string.button_cancel);
470 |
471 | if(WakeOnLanActivity.isTablet == true) {
472 | th.setCurrentTab(1);
473 | }
474 | return true;
475 |
476 | case R.id.menu_delete:
477 | histHandler.deleteHistory(item.id);
478 | return true;
479 |
480 | default:
481 | return super.onContextItemSelected(mi);
482 | }
483 | }
484 |
485 | private void onHistoryItemClick(HistoryItem item) {
486 | String mac = sendPacket(item);
487 | if(mac != null) {
488 | histHandler.incrementHistory(item.id);
489 | }
490 | }
491 |
492 | public static void notifyUser(String message, Context context)
493 | {
494 | if(notification != null) {
495 | notification.setText(message);
496 | notification.show();
497 | } else {
498 | notification = Toast.makeText(context, message, Toast.LENGTH_SHORT);
499 | notification.show();
500 | }
501 | }
502 |
503 | }
504 |
--------------------------------------------------------------------------------