11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Advanced Data Plan
2 | Xposed Framework module
3 | Change Data Usage Cycle date range
4 |
5 | Features
6 | ========
7 |
8 | * Calibrate Android stock Data Usage Summary with your mobile plan
9 | * Check your data usage without 3-party Apps
10 | * Set Daily or Weekly mobile data plan
11 | * Set Data Plan specifying a custom duration in days
12 |
13 | Requirements
14 | ============
15 | Tested with API23, but maybe will work with early versions
16 |
17 | Screenshot
18 | ==========
19 |
20 |
21 |
22 |
23 |
24 |
25 | Support
26 | =======
27 | Please visit [XDA:DevDB Project Thread](https://forum.xda-developers.com/xposed/modules/xposed-advanced-data-plan-t3634138).
28 |
29 | Tested with
30 | ===========
31 | * Cm or Aosp based roms
32 | * TouchWiz roms
33 |
34 | Know issues
35 | ===========
36 | * None :\
37 |
--------------------------------------------------------------------------------
/apk/AdvDataPlan.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m0m4x/AdvDataPlan/9d5f81d34666f5c86fb550420a5677a1bc137d4c/apk/AdvDataPlan.apk
--------------------------------------------------------------------------------
/apk/AdvDataPlan_debug.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m0m4x/AdvDataPlan/9d5f81d34666f5c86fb550420a5677a1bc137d4c/apk/AdvDataPlan_debug.apk
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.2"
6 | defaultConfig {
7 | applicationId "net.m0m4x.android.xposed.advdataplan"
8 | minSdkVersion 23
9 | targetSdkVersion 25
10 | versionCode 6
11 | versionName "1.0.6"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | testCompile 'junit:junit:4.12'
23 | provided 'de.robv.android.xposed:api:82'
24 | provided 'de.robv.android.xposed:api:82:sources'
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in C:\Users\max\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
10 |
13 |
16 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/assets/xposed_init:
--------------------------------------------------------------------------------
1 | net.m0m4x.android.xposed.advdataplan.HookMain
--------------------------------------------------------------------------------
/app/src/main/java/net/m0m4x/android/xposed/advdataplan/HookMain.java:
--------------------------------------------------------------------------------
1 | package net.m0m4x.android.xposed.advdataplan;
2 | /**
3 | * Created by max on 09/04/2017.
4 | */
5 |
6 | import android.annotation.SuppressLint;
7 | import android.app.AlertDialog;
8 | import android.app.DatePickerDialog;
9 | import android.app.DialogFragment;
10 | import android.content.Context;
11 | import android.content.DialogInterface;
12 | import android.content.res.XModuleResources;
13 | import android.os.Build;
14 | import android.text.format.DateUtils;
15 | import android.text.format.Time;
16 | import android.util.Log;
17 | import android.util.TypedValue;
18 | import android.view.Gravity;
19 | import android.view.LayoutInflater;
20 | import android.view.View;
21 | import android.view.ViewGroup;
22 | import android.widget.DatePicker;
23 | import android.widget.LinearLayout;
24 | import android.widget.NumberPicker;
25 | import android.widget.TextView;
26 | import android.widget.Toast;
27 |
28 | import java.lang.reflect.Field;
29 | import java.lang.reflect.Method;
30 | import java.lang.reflect.Modifier;
31 | import java.text.Format;
32 | import java.text.SimpleDateFormat;
33 | import java.util.Calendar;
34 | import java.util.Date;
35 |
36 | import de.robv.android.xposed.IXposedHookInitPackageResources;
37 | import de.robv.android.xposed.IXposedHookLoadPackage;
38 | import de.robv.android.xposed.IXposedHookZygoteInit;
39 | import de.robv.android.xposed.XC_MethodHook;
40 | import de.robv.android.xposed.XC_MethodReplacement;
41 | import de.robv.android.xposed.XposedBridge;
42 | import de.robv.android.xposed.XposedHelpers;
43 | import de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResourcesParam;
44 | import de.robv.android.xposed.callbacks.XC_LayoutInflated;
45 | import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
46 |
47 | import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
48 |
49 |
50 | public class HookMain implements IXposedHookZygoteInit, IXposedHookInitPackageResources, IXposedHookLoadPackage {
51 |
52 | private static final boolean DEBUG = true;
53 |
54 | /****************************
55 | RESOURCES Hooking
56 |
57 | */
58 |
59 | private static String MODULE_PATH = null;
60 |
61 |
62 | static int R_layout_data_usage_cycle_editor;
63 | static int R25_xml_data_usage_cellular;
64 | static int R_id_datepicker;
65 | static int R_id_cycle_days;
66 | static int R_id_cycle_day;
67 |
68 | static int modR_strings_dataplan_days;
69 | static int modR_strings_dataplan_day;
70 | static int modR_strings_nr1_daily;
71 | static int modR_strings_nr7_weekly;
72 | static int modR_strings_nr30_fixedmonth;
73 | static int modR_strings_nr31_monthly;
74 | static int modR_strings_summary_days;
75 | static int modR_strings_summary_starting;
76 | static int modR_strings_cycle_days;
77 | static int modR_strings_cycle_detail;
78 |
79 | @Override
80 | public void initZygote(StartupParam startupParam) throws Throwable {
81 | MODULE_PATH = startupParam.modulePath;
82 | if(DEBUG) XposedBridge.log("HOOK init AdvDataPlan - modulePath:" + startupParam.modulePath + " sdk: "+Build.VERSION.SDK_INT+"");
83 |
84 | }
85 |
86 | @Override
87 | public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {
88 | if(DEBUG) XposedBridge.log("HOOK RES init - " + resparam.packageName + " ! ");
89 | if(!resparam.packageName.equals("com.android.settings")) {
90 | return;
91 | }
92 |
93 | /*
94 | Get ID of module resources
95 |
96 | Marshmallow: native
97 | Nougat: Ok!
98 | */
99 | XModuleResources modRes = XModuleResources.createInstance(MODULE_PATH, resparam.res);
100 | modR_strings_dataplan_days = resparam.res.addResource(modRes, R.string.dataplan_days);
101 | modR_strings_dataplan_day = resparam.res.addResource(modRes, R.string.dataplan_day);
102 | modR_strings_nr1_daily = resparam.res.addResource(modRes, R.string.nr1_daily);
103 | modR_strings_nr7_weekly = resparam.res.addResource(modRes, R.string.nr7_weekly);
104 | modR_strings_nr30_fixedmonth = resparam.res.addResource(modRes, R.string.nr30_fixedmonth);
105 | modR_strings_nr31_monthly = resparam.res.addResource(modRes, R.string.nr31_monthly);
106 | modR_strings_summary_days = resparam.res.addResource(modRes, R.string.summary_days);
107 | modR_strings_summary_starting = resparam.res.addResource(modRes, R.string.summary_starting);
108 | modR_strings_cycle_days = resparam.res.addResource(modRes, R.string.cycle_days);
109 | modR_strings_cycle_detail = resparam.res.addResource(modRes, R.string.cycle_detail);
110 |
111 | /*
112 | Get ID of native resources
113 |
114 | Marshmallow: native
115 | Nougat: Ok!
116 | */
117 | R_layout_data_usage_cycle_editor = resparam.res.getIdentifier("data_usage_cycle_editor", "layout", "com.android.settings");
118 | if(DEBUG) XposedBridge.log("HOOK RES ...found R.layout.data_usage_cycle_editor : " + R_layout_data_usage_cycle_editor + " !");
119 |
120 | R25_xml_data_usage_cellular = resparam.res.getIdentifier("data_usage_cellular", "xml", "com.android.settings");
121 | if(DEBUG) XposedBridge.log("HOOK RES ...found R.layout.data_usage_cellular : " + R25_xml_data_usage_cellular + " !");
122 |
123 | /*
124 | Hook Layout 'data_usage_cycle_editor'
125 |
126 | Marshmallow: native
127 | Nougat: Ok!
128 | */
129 | resparam.res.hookLayout("com.android.settings", "layout", "data_usage_cycle_editor", new XC_LayoutInflated() {
130 | @SuppressLint("NewApi")
131 | @Override
132 | public void handleLayoutInflated(LayoutInflatedParam liparam) throws Throwable {
133 | if(DEBUG) XposedBridge.log("HOOK RES handleLayoutInflated(): Layout is inflating... - data_usage_cycle_editor!");
134 |
135 | Context context = liparam.view.getContext();
136 |
137 | /*
138 | layout 0 : [
139 | txtview <= original (visibility GONE)
140 | numpicker <= original (visibility GONE)
141 | + layout 1 [ datepicker ]
142 | + layout 2 [txtview numpicker]
143 | ]
144 | */
145 | int i150dip = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 150, context.getResources().getDisplayMetrics());
146 | int i100dip = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, context.getResources().getDisplayMetrics());
147 | int i48dip = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, context.getResources().getDisplayMetrics());
148 | int i16dip = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, context.getResources().getDisplayMetrics());
149 |
150 | try {
151 |
152 | //numberPicker
153 | R_id_cycle_day = liparam.res.getIdentifier("cycle_day", "id", "com.android.settings");
154 | Object l0_num = liparam.view.findViewById(R_id_cycle_day);
155 | XposedHelpers.callMethod(l0_num, "setVisibility", View.GONE);
156 |
157 | //debug
158 | //if (DEBUG) view_dump(l0_num);
159 |
160 | //layout 0 (root - ViewGroup)
161 | ViewGroup res_layout0 = (ViewGroup) XposedHelpers.callMethod(l0_num, "getParent");
162 | if(DEBUG) XposedBridge.log("HOOK RES handleLayoutInflated(): Parent layout is " + res_layout0.getClass().getName());
163 | //case: LinearLayout - set Vertical
164 | if (res_layout0 instanceof LinearLayout) {
165 | if(DEBUG) XposedBridge.log("HOOK RES handleLayoutInflated(): LinearLayout instance detected.");
166 | LinearLayout res_lin_layout0 = (LinearLayout) res_layout0;
167 | res_lin_layout0.setOrientation(LinearLayout.VERTICAL);
168 | }
169 |
170 | //hide originals layouts
171 | try {
172 | for (int i = 0; i < res_layout0.getChildCount(); i++) {
173 | res_layout0.getChildAt(i).setVisibility(View.GONE);
174 | }
175 | } catch(Exception ex) {
176 | XposedBridge.log("HOOK WARNING handleLayoutInflated(): Cannot empty existent layout!");
177 | StackTraceElement[] elements = ex.getStackTrace();
178 | XposedBridge.log("HOOK WARNING handleLayoutInflated(): " + ex.getMessage());
179 | XposedBridge.log("HOOK WARNING handleLayoutInflated(): at " + elements[0].getClassName() + "." + elements[0].getMethodName() + "() line: " + elements[0].getLineNumber());
180 | }
181 |
182 | //layout 1
183 | LinearLayout res_layout1 = new LinearLayout(context);
184 | res_layout1.setOrientation(LinearLayout.HORIZONTAL);
185 | TextView l1_txt = new TextView(context);
186 | LinearLayout.LayoutParams l1_txt_lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f);
187 | l1_txt_lp.gravity = Gravity.CENTER_VERTICAL;
188 | l1_txt_lp.setMarginStart(i16dip);
189 | l1_txt.setLayoutParams(l1_txt_lp);
190 | l1_txt.setGravity(Gravity.CENTER_VERTICAL);
191 | //l2_txt.setTextAppearance(context, android.R.style.TextAppearance_DeviceDefault_Medium);
192 | l1_txt.setText(modR_strings_dataplan_day);
193 | DatePickerDialog l1_dat = new DatePickerDialog(context, android.R.style.Theme_Holo_Light_Dialog, null, 2017,4,16);
194 | l1_dat.getDatePicker().findViewById(context.getResources().getIdentifier("year","id","android")).setVisibility(View.GONE);
195 | l1_dat.getDatePicker().setId(View.generateViewId());
196 | R_id_datepicker = l1_dat.getDatePicker().getId();
197 | l1_dat.getDatePicker().setLayoutParams(new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f));
198 | l1_dat.getDatePicker().setCalendarViewShown(false);
199 | l1_dat.getDatePicker().setSpinnersShown(true);
200 | //l1_dat.getDatePicker().setVisibility(View.GONE);
201 | res_layout1.addView(l1_txt);
202 | res_layout1.addView(l1_dat.getDatePicker());
203 |
204 | //layout 2
205 | LinearLayout res_layout2 = new LinearLayout(context);
206 | res_layout2.setOrientation(LinearLayout.HORIZONTAL);
207 | TextView l2_txt = new TextView(context);
208 | LinearLayout.LayoutParams l2_txt_lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f);
209 | l2_txt_lp.gravity = Gravity.CENTER_VERTICAL;
210 | l2_txt_lp.setMarginStart(i16dip);
211 | l2_txt.setLayoutParams(l2_txt_lp);
212 | l2_txt.setGravity(Gravity.CENTER_VERTICAL);
213 | //l2_txt.setTextAppearance(context, android.R.style.TextAppearance_DeviceDefault_Medium);
214 | l2_txt.setText(modR_strings_dataplan_days);
215 | NumberPicker l2_num = new NumberPicker(context);
216 | l2_num.setId(View.generateViewId());
217 | R_id_cycle_days = l2_num.getId();
218 | LinearLayout.LayoutParams l2_num_lp = new LinearLayout.LayoutParams( i150dip, i100dip);
219 | l2_num_lp.setMarginEnd(i16dip);
220 | l2_num_lp.setMarginStart(i16dip);
221 | l2_num.setLayoutParams(l2_num_lp);
222 | l2_num.setGravity(Gravity.CENTER_VERTICAL);
223 | res_layout2.addView(l2_txt);
224 | res_layout2.addView(l2_num);
225 |
226 | //Adding Layouts
227 | (res_layout0).addView(res_layout1);
228 | (res_layout0).addView(res_layout2);
229 |
230 | } catch (Exception ex) {
231 | XposedBridge.log("HOOK ERROR handleLayoutInflated(): " + ex.getMessage());
232 | StackTraceElement[] elements = ex.getStackTrace();
233 | XposedBridge.log("HOOK ERROR handleLayoutInflated(): at " + elements[0].getClassName() + "." + elements[0].getMethodName() + "() line: " + elements[0].getLineNumber());
234 | throw ex;
235 | }
236 |
237 | if(DEBUG) XposedBridge.log("HOOK RES handleLayoutInflated(): Layout is inflated!");
238 | }
239 | });
240 |
241 | }
242 |
243 |
244 |
245 |
246 |
247 | /****************************
248 | METHODS Hooking
249 |
250 | */
251 |
252 | public static final int CYCLE_NONE = -1;
253 | private static final String EXTRA_TEMPLATE = "template";
254 |
255 | public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
256 |
257 | if(DEBUG) XposedBridge.log("HOOK handleLoadPackage - " + lpparam.packageName + "! " );
258 |
259 | /*
260 | NetworkPolicy Classes - Compute cycle Boundaries
261 |
262 | */
263 | if( lpparam.packageName.equals("android")
264 | || lpparam.packageName.equals("com.android.systemui")
265 | || lpparam.packageName.equals("com.android.settings")
266 | || lpparam.packageName.equals("com.android.providers.settings")
267 | ){
268 | if(DEBUG) XposedBridge.log("HOOK NetworkPolicyManager methods! (pkg:"+lpparam.packageName+")");
269 |
270 | final Class> NetworkPolicyManager = XposedHelpers.findClass(
271 | "android.net.NetworkPolicyManager",
272 | lpparam.classLoader);
273 | final Class> NetworkPolicy = XposedHelpers.findClass(
274 | "android.net.NetworkPolicy",
275 | lpparam.classLoader);
276 |
277 | findAndHookMethod(NetworkPolicyManager, "computeNextCycleBoundary", long.class , NetworkPolicy , new XC_MethodReplacement() {
278 |
279 | @Override
280 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
281 | if(DEBUG) XposedBridge.log("HOOK REQ computeNextCycleBoundary(): (pkg:"+lpparam.packageName+")");
282 |
283 | // Get Params
284 | long currentTime = (long) param.args[0]; // long currentTime
285 | Object policy = param.args[1]; // NetworkPolicy policy
286 | int cycle_day = (int) XposedHelpers.getObjectField(policy, "cycleDay");
287 |
288 | // Check
289 | if (cycle_day == CYCLE_NONE) {
290 | throw new IllegalArgumentException("Unable to compute boundary without cycleDay");
291 | }
292 |
293 | return mComputeNextCycleBoundary(currentTime, cycle_day);
294 |
295 | }
296 |
297 | });
298 |
299 | findAndHookMethod(NetworkPolicyManager, "computeLastCycleBoundary", long.class , NetworkPolicy , new XC_MethodReplacement() {
300 |
301 | @Override
302 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
303 | if(DEBUG) XposedBridge.log("HOOK REQ computeLastCycleBoundary(): (pkg:"+lpparam.packageName+")");
304 |
305 | // Get Params
306 | long currentTime = (long) param.args[0]; // long currentTime
307 | Object policy = param.args[1]; // NetworkPolicy policy
308 | int cycle_day = (int) XposedHelpers.getObjectField(policy, "cycleDay");
309 |
310 | // Need to Check policy template mobile? (otherwise do default)
311 |
312 | // Check
313 | if (cycle_day == CYCLE_NONE) {
314 | throw new IllegalArgumentException("Unable to compute boundary without cycleDay");
315 | }
316 | return mComputeLastCycleBoundary(currentTime, cycle_day);
317 |
318 | }
319 |
320 | });
321 |
322 | }
323 |
324 | /*
325 | DataUsage Classes - Editor Dialog Fragment
326 |
327 | */
328 | if( lpparam.packageName.equals("com.android.settings") ) {
329 | if(DEBUG) XposedBridge.log("HOOK DataUsageSummary/BillingCycleSettings methods! (pkg:"+lpparam.packageName+")!");
330 |
331 | //SDK23
332 | if (Build.VERSION.SDK_INT == 23) {
333 | final Class> CycleEditorFragment = XposedHelpers.findClass(
334 | "com.android.settings.DataUsageSummary.CycleEditorFragment",
335 | lpparam.classLoader);
336 | findAndHookMethod(CycleEditorFragment, "onCreateDialog", "android.os.Bundle", new XC_MethodReplacement() {
337 | @Override
338 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
339 | if (DEBUG) XposedBridge.log("HOOK onCreateDialog(): Called now!");
340 |
341 | try {
342 | return createAdvDialog(param);
343 | } catch (Exception ex) {
344 | XposedBridge.log("HOOK ERROR createAdvDialog(): " + ex.getMessage());
345 | StackTraceElement[] elements = ex.getStackTrace();
346 | XposedBridge.log("HOOK ERROR createAdvDialog(): at " + elements[0].getClassName() + "." + elements[0].getMethodName() + "() line: " + elements[0].getLineNumber());
347 | String dump=obj_dump(param.thisObject);
348 | XposedBridge.log("HOOK ERROR createAdvDialog() objDUMP: Dumping DataUsageSummary.CycleEditorFragment ...");
349 | for (String item : dump.split(System.getProperty("line.separator"))) {
350 | XposedBridge.log("HOOK ERROR createAdvDialog() objDUMP: " + item);
351 | }
352 | throw ex;
353 | }
354 |
355 | }
356 |
357 | });
358 | }
359 |
360 | //SDK24-25
361 | if (Build.VERSION.SDK_INT == 24 || Build.VERSION.SDK_INT == 25) {
362 | final Class> CycleEditorFragment = XposedHelpers.findClass(
363 | "com.android.settings.datausage.BillingCycleSettings.CycleEditorFragment",
364 | lpparam.classLoader);
365 | findAndHookMethod(CycleEditorFragment, "onCreateDialog", "android.os.Bundle", new XC_MethodReplacement() {
366 | @Override
367 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
368 | if (DEBUG) XposedBridge.log("HOOK onCreateDialog(): Starting here! " + lpparam.packageName);
369 |
370 | try {
371 | return createAdvDialog(param);
372 |
373 | } catch (Exception ex) {
374 | XposedBridge.log("HOOK ERROR createAdvDialog(): " + ex.getMessage());
375 | StackTraceElement[] elements = ex.getStackTrace();
376 | XposedBridge.log("HOOK ERROR createAdvDialog(): at " + elements[0].getClassName() + "." + elements[0].getMethodName() + "() line: " + elements[0].getLineNumber());
377 | String dump=obj_dump(param.thisObject);
378 | XposedBridge.log("HOOK ERROR createAdvDialog() objDUMP: Dumping BillingCycleSettings.CycleEditorFragment ...");
379 | for (String item : dump.split(System.getProperty("line.separator"))) {
380 | XposedBridge.log("HOOK ERROR createAdvDialog() objDUMP: " + item);
381 | }
382 | throw ex;
383 | }
384 |
385 | }
386 | });
387 | findAndHookMethod(CycleEditorFragment, "onClick", DialogInterface.class, int.class , new XC_MethodReplacement() {
388 | @Override
389 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
390 | if (DEBUG) XposedBridge.log("HOOK onClick START!" + lpparam.packageName);
391 |
392 | try {
393 | final Object args = XposedHelpers.callMethod(param.thisObject, "getArguments");
394 | final Object template = XposedHelpers.callMethod(args, "getParcelable", EXTRA_TEMPLATE ); //type NetworkTemplate
395 | final Object target = XposedHelpers.callMethod(param.thisObject, "getTargetFragment"); //type sdk24:BillingCycleSettings sdk25:DataUsageEditController
396 |
397 | final Object editor; //type NetworkPolicyEditor
398 | if (Build.VERSION.SDK_INT == 24) {
399 | Object services = XposedHelpers.getObjectField(target,"services");
400 | editor = XposedHelpers.getObjectField(services,"mPolicyEditor");
401 | } else if (Build.VERSION.SDK_INT == 25) {
402 | editor = XposedHelpers.callMethod(target, "getNetworkPolicyEditor");
403 | } else {
404 | XposedBridge.log("HOOK AdvDialog.onClick(): SDK "+Build.VERSION.SDK_INT+" not supported!");
405 | return null;
406 | }
407 |
408 | final NumberPicker cycleDayPicker = (NumberPicker) XposedHelpers.getObjectField(param.thisObject, "mCycleDayPicker");
409 | final NumberPicker cycleDaysPicker = (NumberPicker) XposedHelpers.getAdditionalStaticField(param.thisObject, "mCycleDaysPicker");
410 | final DatePicker cycleDatePicker = (DatePicker) XposedHelpers.getAdditionalStaticField(param.thisObject, "mCycleDatePicker");
411 |
412 | // clear focus to finish pending text edits
413 | cycleDayPicker.clearFocus();
414 | cycleDaysPicker.clearFocus();
415 | cycleDatePicker.clearFocus();
416 |
417 | // Encode Day of Month, Month and Duration into one int
418 | // via BitShift method.
419 | int bs = encodeBitShiftedInt(cycleDatePicker, cycleDaysPicker);
420 |
421 | //Save in policy CycleDay
422 | final String cycleTimezone = new Time().timezone;
423 | XposedHelpers.callMethod(editor, "setPolicyCycleDay", template, bs, cycleTimezone);
424 |
425 | if (Build.VERSION.SDK_INT == 24) {
426 | XposedHelpers.callMethod(target, "updatePrefs");
427 | } else if (Build.VERSION.SDK_INT == 25) {
428 | XposedHelpers.callMethod(target, "updateDataUsage");
429 | }
430 |
431 | return null;
432 |
433 | } catch (Exception ex) {
434 | XposedBridge.log("HOOK ERROR AdvDialog.onClick(): " + ex.getMessage());
435 | StackTraceElement[] elements = ex.getStackTrace();
436 | XposedBridge.log("HOOK ERROR AdvDialog.onClick(): at " + elements[0].getClassName() + "." + elements[0].getMethodName() + "() line: " + elements[0].getLineNumber());
437 | String dump=obj_dump(param.thisObject);
438 | XposedBridge.log("HOOK ERROR AdvDialog.onClick() objDUMP: Dumping BillingCycleSettings.CycleEditorFragment ...");
439 | for (String item : dump.split(System.getProperty("line.separator"))) {
440 | XposedBridge.log("HOOK ERROR AdvDialog.onClick() objDUMP: " + item);
441 | }
442 | throw ex;
443 | }
444 |
445 | }
446 | });
447 |
448 | /*
449 | findAndHookMethod(CycleEditorFragment, "show", BillingCycleSettings, new XC_MethodReplacement() {
450 | @Override
451 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
452 | if (DEBUG) XposedBridge.log("HOOK show START!" + lpparam.packageName);
453 |
454 | return null;
455 | }
456 | });
457 | */
458 |
459 | }
460 |
461 | }
462 |
463 | /*
464 | System UI - getDataUsageInfo
465 |
466 | */
467 |
468 | if( lpparam.packageName.equals("com.android.systemui")
469 | || lpparam.packageName.equals("com.android.settings")
470 | ){
471 |
472 | if(DEBUG) XposedBridge.log("HOOK SystemUI methods! (pkg:"+lpparam.packageName+")!");
473 |
474 | //SDK22
475 | // Lollipop not supported
476 | // CLASS_MOBILE_DATA_CONTROLLER_23 = "com.android.systemui.statusbar.policy.MobileDataControllerImpl";
477 | // CLASS_MOBILE_DATA_CONTROLLER_22 = "com.android.systemui.statusbar.policy.MobileDataController";
478 | // Build.VERSION.SDK_INT >= 22 ? CLASS_MOBILE_DATA_CONTROLLER_23 : CLASS_MOBILE_DATA_CONTROLLER_22;
479 |
480 | //SDK23
481 | if (Build.VERSION.SDK_INT == 23) {
482 | final Class> MobileDataController = XposedHelpers.findClass(
483 | "com.android.systemui.statusbar.policy.MobileDataControllerImpl",
484 | lpparam.classLoader);
485 | final Class> NetworkTemplate = XposedHelpers.findClass(
486 | "android.net.NetworkTemplate",
487 | lpparam.classLoader);
488 | final Class> NetworkStatsHistory = XposedHelpers.findClass(
489 | "android.net.NetworkStatsHistory",
490 | lpparam.classLoader);
491 | final Class> DataUsageInfo = XposedHelpers.findClass(
492 | "com.android.systemui.statusbar.policy.NetworkController$MobileDataController$DataUsageInfo",
493 | lpparam.classLoader);
494 | findAndHookMethod(MobileDataController, "getDataUsageInfo", new XC_MethodReplacement() {
495 | @Override
496 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
497 | if (DEBUG)
498 | XposedBridge.log("HOOK UI MobileDataController.getDataUsageInfo(): (pkg:" + lpparam.packageName + ")");
499 |
500 | return getAdvDataUsageInfo(param, NetworkTemplate, NetworkStatsHistory, DataUsageInfo);
501 | }
502 | });
503 | }
504 |
505 |
506 | //SDK24-25
507 | if (Build.VERSION.SDK_INT == 24 || Build.VERSION.SDK_INT == 25) {
508 | final Class> DataUsageController = XposedHelpers.findClass(
509 | "com.android.settingslib.net.DataUsageController",
510 | lpparam.classLoader);
511 | final Class> NetworkTemplate = XposedHelpers.findClass(
512 | "android.net.NetworkTemplate",
513 | lpparam.classLoader);
514 | final Class> NetworkStatsHistory = XposedHelpers.findClass(
515 | "android.net.NetworkStatsHistory",
516 | lpparam.classLoader);
517 | final Class> DataUsageInfo = XposedHelpers.findClass(
518 | "com.android.settingslib.net.DataUsageController.DataUsageInfo",
519 | lpparam.classLoader);
520 | findAndHookMethod(DataUsageController, "getDataUsageInfo", NetworkTemplate, new XC_MethodReplacement() {
521 | @Override
522 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
523 | if (DEBUG)
524 | XposedBridge.log("HOOK UI DataUsageController.getDataUsageInfo(): (pkg:" + lpparam.packageName + ")");
525 |
526 | return getAdvDataUsageInfo(param, NetworkTemplate, NetworkStatsHistory, DataUsageInfo);
527 | }
528 | });
529 |
530 | }
531 |
532 | }
533 |
534 |
535 |
536 | /*
537 | System UI - Settings App - Billing Cycle preview
538 | only in SDK 24-25
539 | */
540 |
541 | if (Build.VERSION.SDK_INT == 24 || Build.VERSION.SDK_INT == 25) {
542 | if( false|| lpparam.packageName.equals("com.android.settings")
543 | || lpparam.packageName.equals("com.android.settings.datausage")
544 | ) {
545 |
546 | //BillingCyclePreference.java
547 | // com.android.settings.datausage.BillingCyclePreference
548 | // extend android.support.v7.preference.Preference
549 | final Class> Preferencev7 = XposedHelpers.findClass(
550 | "android.support.v7.preference.Preference",
551 | lpparam.classLoader);
552 | findAndHookMethod(Preferencev7, "setSummary", CharSequence.class, new XC_MethodHook() {
553 | @Override
554 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
555 | if (DEBUG)XposedBridge.log("HOOK UI Preference.setSummary(): class "+param.thisObject.getClass().getName().toString()+" (pkg:" + lpparam.packageName + ")");
556 |
557 | //com.android.settings.datausage.BillingCyclePreference
558 | if (param.thisObject.getClass().getName().equals("com.android.settings.datausage.BillingCyclePreference")){
559 |
560 | try{
561 | //Get CycleDay
562 | Object policy = XposedHelpers.getObjectField(param.thisObject, "mPolicy");
563 | int cycleDay = 1;
564 | if(policy != null) cycleDay = (int) XposedHelpers.getObjectField(policy, "cycleDay");
565 |
566 | //Decode CycleDay
567 | Object[] decodedArr = decodeBitShiftedInt(cycleDay);
568 | Calendar pref_cycleDate = (Calendar) decodedArr[0];
569 | int pref_cycleDays = (int) decodedArr[1];
570 |
571 | //Build phrase
572 | final Context context = (Context) XposedHelpers.callMethod(param.thisObject, "getContext");
573 | String strDays;
574 | switch (pref_cycleDays) {
575 | case 1: strDays = (String) XposedHelpers.callMethod(context, "getString", modR_strings_nr1_daily);
576 | break;
577 | case 7: strDays = (String) XposedHelpers.callMethod(context, "getString", modR_strings_nr7_weekly);
578 | break;
579 | case 31: strDays = (String) XposedHelpers.callMethod(context, "getString", modR_strings_nr31_monthly);
580 | break;
581 | default: strDays = String.format((String) XposedHelpers.callMethod(context, "getString", modR_strings_summary_days), pref_cycleDays);
582 | break;
583 | }
584 |
585 | Format format = new SimpleDateFormat("dd MMM yyyy");
586 | param.args[0] = strDays + " " + String.format( (String) XposedHelpers.callMethod(context, "getString", modR_strings_summary_starting), format.format(new Date(pref_cycleDate.getTimeInMillis())));
587 |
588 | } catch (Exception ex) {
589 | XposedBridge.log("HOOK ERROR Preference.setSummary(): " + ex.getMessage());
590 | StackTraceElement[] elements = ex.getStackTrace();
591 | XposedBridge.log("HOOK ERROR Preference.setSummary(): at " + elements[0].getClassName() + "." + elements[0].getMethodName() + "() line: " + elements[0].getLineNumber());
592 | String dump=obj_dump(param.thisObject);
593 | XposedBridge.log("HOOK ERROR Preference.setSummary() objDUMP: Dumping Preferencev7 ...");
594 | for (String item : dump.split(System.getProperty("line.separator"))) {
595 | XposedBridge.log("HOOK ERROR Preference.setSummary() objDUMP: " + item);
596 | }
597 | throw ex;
598 | }
599 | }
600 |
601 | }
602 | });
603 |
604 | //BillingCycleSettings.java
605 | // com.android.settings.datausage.BillingCycleSettings
606 | // calls android.support.v7.preference.Preference setSummary()
607 | final Class> BillingCycleSettings = XposedHelpers.findClass(
608 | "com.android.settings.datausage.BillingCycleSettings",
609 | lpparam.classLoader);
610 | findAndHookMethod(BillingCycleSettings, "updatePrefs", new XC_MethodHook() {
611 | @Override
612 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
613 | if (DEBUG)XposedBridge.log("HOOK UI BillingCycleSettings.updatePrefs(): (pkg:" + lpparam.packageName + ")");
614 |
615 | try {
616 | Object template = XposedHelpers.getObjectField(param.thisObject,"mNetworkTemplate");
617 | Object services = XposedHelpers.getObjectField(param.thisObject,"services");
618 | Object editor = XposedHelpers.getObjectField(services,"mPolicyEditor");
619 | Object policy = XposedHelpers.callMethod(editor,"getPolicy",template);
620 |
621 | //Get CycleDay
622 | int cycleDay = 1;
623 | if(policy != null) cycleDay = (int) XposedHelpers.getObjectField(policy, "cycleDay");
624 |
625 | //Decode CycleDay
626 | Object[] decodedArr = decodeBitShiftedInt(cycleDay);
627 | Calendar pref_cycleDate = (Calendar) decodedArr[0];
628 | Calendar pref_cycleDateEnd = Calendar.getInstance();
629 | pref_cycleDateEnd.setTimeInMillis(mComputeNextCycleBoundary(pref_cycleDate.getTimeInMillis(), cycleDay));
630 | int pref_cycleDays = (int) decodedArr[1];
631 |
632 | //Build phrase
633 | final Context context = (Context) XposedHelpers.callMethod(param.thisObject, "getContext");
634 | String strDays;
635 | switch (pref_cycleDays) {
636 | case 1: strDays = (String) XposedHelpers.callMethod(context, "getString", modR_strings_nr1_daily);
637 | break;
638 | case 7: strDays = (String) XposedHelpers.callMethod(context, "getString", modR_strings_nr7_weekly);
639 | break;
640 | case 31: strDays = (String) XposedHelpers.callMethod(context, "getString", modR_strings_nr31_monthly);
641 | break;
642 | default: strDays = String.format((String) XposedHelpers.callMethod(context, "getString", modR_strings_cycle_days), Integer.toString(pref_cycleDays));
643 | break;
644 | }
645 |
646 | Format format = new SimpleDateFormat("dd MMM");
647 | Object pref = XposedHelpers.getObjectField(param.thisObject, "mBillingCycle");
648 | XposedHelpers.callMethod(pref, "setSummary", String.format((String) XposedHelpers.callMethod(context, "getString", modR_strings_cycle_detail), format.format(new Date(pref_cycleDate.getTimeInMillis())), format.format(new Date(pref_cycleDateEnd.getTimeInMillis())), strDays.toLowerCase() ));
649 |
650 | } catch (Exception ex) {
651 | XposedBridge.log("HOOK ERROR BillingCycleSettings.updatePrefs(): " + ex.getMessage());
652 | StackTraceElement[] elements = ex.getStackTrace();
653 | XposedBridge.log("HOOK ERROR BillingCycleSettings.updatePrefs(): at " + elements[0].getClassName() + "." + elements[0].getMethodName() + "() line: " + elements[0].getLineNumber());
654 | String dump=obj_dump(param.thisObject);
655 | XposedBridge.log("HOOK ERROR BillingCycleSettings.updatePrefs() objDUMP: Dumping BillingCycleSettings ...");
656 | for (String item : dump.split(System.getProperty("line.separator"))) {
657 | XposedBridge.log("HOOK ERROR BillingCycleSettings.updatePrefs() objDUMP: " + item);
658 | }
659 | throw ex;
660 | }
661 |
662 | }
663 | });
664 |
665 | }
666 | }
667 |
668 | }
669 |
670 | /****************************
671 | Main Methods
672 |
673 | */
674 |
675 | //Compute methods
676 | private static long mComputeLastCycleBoundary(long currentTime, int cycleDay) {
677 | //Decode CycleDay
678 | Object[] decodedArr = decodeBitShiftedInt(cycleDay);
679 | Calendar pref_cycleDate = (Calendar) decodedArr[0];
680 | int pref_cycleDays = (int) decodedArr[1];
681 |
682 | //Debug - Wait... What is the request?
683 | if(DEBUG) { Format format = new SimpleDateFormat("dd/MM/yyyy");
684 | XposedBridge.log("HOOK REQ LAST Cycle with prefTime:"+format.format(new Date(pref_cycleDate.getTimeInMillis()))+
685 | "*"+pref_cycleDays+" for currentTime:"+format.format(new Date(currentTime))); }
686 |
687 | // Approach to date currentTime, when i am close choose Last <- or Next ->
688 | Calendar cycleDate = (Calendar) pref_cycleDate.clone();
689 | int m;
690 | if (cycleDate.getTimeInMillis()>=currentTime) m = -1; else m = 1;
691 | while ( daysBetween(cycleDate.getTimeInMillis(), currentTime) >= pref_cycleDays ) {
692 | cycleDate.add(Calendar.DAY_OF_YEAR, pref_cycleDays * m);
693 | }
694 | // Set Last Cycle
695 | if(cycleDate.getTimeInMillis()>=currentTime) cycleDate.add(Calendar.DAY_OF_YEAR, -pref_cycleDays);
696 |
697 | //Debug - Ok... That's my result.
698 | if(DEBUG) { Format format = new SimpleDateFormat("dd/MM/yyyy");
699 | XposedBridge.log("HOOK from prefTime:"+format.format(new Date(pref_cycleDate.getTimeInMillis()))+
700 | " LAST to currentTime:"+format.format(new Date(currentTime))+
701 | " is "+format.format(new Date(cycleDate.getTimeInMillis())) ); }
702 |
703 | //Return Last
704 | return cycleDate.getTimeInMillis();
705 | }
706 |
707 | private static long mComputeNextCycleBoundary(long currentTime, int cycleDay) {
708 | //Decode CycleDay
709 | Object[] decodedArr = decodeBitShiftedInt(cycleDay);
710 | Calendar pref_cycleDate = (Calendar) decodedArr[0];
711 | int pref_cycleDays = (int) decodedArr[1];
712 |
713 | //Debug - Wait... What is the request?
714 | if(DEBUG) { Format format = new SimpleDateFormat("dd/MM/yyyy");
715 | XposedBridge.log("HOOK REQ NEXT Cycle with prefTime:"+format.format(new Date(pref_cycleDate.getTimeInMillis()))+
716 | "*"+pref_cycleDays+" for currentTime:"+format.format(new Date(currentTime))); }
717 |
718 | // Approach to date currentTime, when i am close choose Last <- or Next ->
719 | Calendar cycleDate = (Calendar) pref_cycleDate.clone();
720 | int m;
721 | if (cycleDate.getTimeInMillis()>currentTime) m = -1; else m = 1;
722 | while ( daysBetween(cycleDate.getTimeInMillis(), currentTime) >= pref_cycleDays ) {
723 | if(DEBUG) XposedBridge.log("HOOK " + m + " " + pref_cycleDays );
724 | cycleDate.add(Calendar.DAY_OF_YEAR, pref_cycleDays * m);
725 | }
726 | // Set Next Cycle
727 | if(cycleDate.getTimeInMillis()<=currentTime) cycleDate.add(Calendar.DAY_OF_YEAR, pref_cycleDays);
728 |
729 | //Debug - Ok... That's my result.
730 | if(DEBUG) { Format format = new SimpleDateFormat("dd/MM/yyyy");
731 | XposedBridge.log("HOOK from prefTime:"+format.format(new Date(pref_cycleDate.getTimeInMillis()))+
732 | " NEXT to currentTime:"+format.format(new Date(currentTime))+
733 | " is "+format.format(new Date(cycleDate.getTimeInMillis())) ); }
734 |
735 | //Return Last
736 | return cycleDate.getTimeInMillis();
737 | }
738 |
739 | //Dialog methods
740 | private static Object createAdvDialog(XC_MethodHook.MethodHookParam param) {
741 |
742 | XposedBridge.log("HOOK createAdvDialog(): start!");
743 |
744 | DialogFragment mCycleEditorFragment = (DialogFragment) param.thisObject; //CycleEditorFragment
745 | final Context context = (Context) XposedHelpers.callMethod(param.thisObject, "getActivity");
746 |
747 | final Object target = XposedHelpers.callMethod(param.thisObject, "getTargetFragment"); //type sdk:23 DataUsageSummary sdk:24 BillingCycleSettings sdk:25 DataUsageEditController
748 | final Object editor; //type NetworkPolicyEditor
749 | if (Build.VERSION.SDK_INT == 23) {
750 | editor = XposedHelpers.getObjectField(target, "mPolicyEditor");
751 | } else if (Build.VERSION.SDK_INT == 24) {
752 | Object services = XposedHelpers.getObjectField(target,"services");
753 | editor = XposedHelpers.getObjectField(services,"mPolicyEditor");
754 | } else if (Build.VERSION.SDK_INT == 25) {
755 | editor = XposedHelpers.callMethod(target, "getNetworkPolicyEditor");
756 | } else {
757 | XposedBridge.log("HOOK createAdvDialog(): SDK "+Build.VERSION.SDK_INT+" not supported!");
758 | return null;
759 | }
760 |
761 | final AlertDialog.Builder builder = new AlertDialog.Builder(context);
762 | final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
763 | final View view = (View) XposedHelpers.callMethod(dialogInflater, "inflate", R_layout_data_usage_cycle_editor, null, false);
764 |
765 | if(DEBUG) XposedBridge.log("HOOK createAdvDialog(): R_id_cycle_day="+R_id_cycle_day);
766 | if(DEBUG) XposedBridge.log("HOOK createAdvDialog(): R_id_cycle_days="+R_id_cycle_days);
767 | if(DEBUG) XposedBridge.log("HOOK createAdvDialog(): R_id_datepicker="+R_id_datepicker);
768 | //view_dump(view);
769 |
770 | //get original cycleDayPicker (useless)
771 | final Object cycleDayPicker;
772 | if (Build.VERSION.SDK_INT == 23) {
773 | cycleDayPicker = view.findViewById(R_id_cycle_day);
774 | } else if (Build.VERSION.SDK_INT == 24 || Build.VERSION.SDK_INT == 25) {
775 | XposedHelpers.setObjectField(param.thisObject, "mCycleDayPicker", view.findViewById(R_id_cycle_day));
776 | cycleDayPicker = XposedHelpers.getObjectField(param.thisObject, "mCycleDayPicker");
777 | } else {
778 | Toast.makeText(context, "SDK "+Build.VERSION.SDK_INT+" not supported!", Toast.LENGTH_LONG).show();
779 | return null;
780 | }
781 |
782 | //Get cycleDay
783 | final Object args = XposedHelpers.callMethod(param.thisObject, "getArguments");
784 | final Object template = XposedHelpers.callMethod(args, "getParcelable", EXTRA_TEMPLATE ); //type NetworkTemplate
785 | final int cycleDay = (int) XposedHelpers.callMethod(editor, "getPolicyCycleDay", template);
786 |
787 | //Decode cycleDay
788 | Object[] decodedArr = decodeBitShiftedInt(cycleDay);
789 | Calendar pref_cycle_date = (Calendar) decodedArr[0];
790 | int pref_cycle_days = (int) decodedArr[1];
791 |
792 | //Update pref_cycle_date to Last Cycle
793 | while (pref_cycle_date.getTimeInMillis() < System.currentTimeMillis()) { // Cycle until cycle_date > currentTime
794 | pref_cycle_date.add(Calendar.DAY_OF_MONTH, pref_cycle_days);
795 | if (DEBUG) {
796 | Format ft = new SimpleDateFormat("dd MMM yyyy");
797 | XposedBridge.log("HOOK createAdvDialog(): pref_cycle_date updated to " + ft.format(new Date(pref_cycle_date.getTimeInMillis())) + "");
798 | }
799 | }
800 | pref_cycle_date.add(Calendar.DAY_OF_MONTH, -pref_cycle_days); //Set Last Cycle
801 |
802 | //Set layout cycleDayPicker (useless)
803 | try {
804 | if (DEBUG) XposedBridge.log("HOOK createAdvDialog(): cycleDayPicker id = " + XposedHelpers.callMethod(cycleDayPicker, "getId") + " " + cycleDayPicker.getClass().toString());
805 | XposedHelpers.callMethod(cycleDayPicker, "setMinValue", 1);
806 | XposedHelpers.callMethod(cycleDayPicker, "setMaxValue", 365);
807 | XposedHelpers.callMethod(cycleDayPicker, "setValue", cycleDay);
808 | XposedHelpers.callMethod(cycleDayPicker, "setWrapSelectorWheel", true);
809 | } catch (Exception ex) {
810 | XposedBridge.log("HOOK ERROR createAdvDialog(): Updating cycleDayPicker - StackTrace: " + ex.getMessage());
811 | StackTraceElement[] elements = ex.getStackTrace();
812 | XposedBridge.log("HOOK ERROR createAdvDialog(): at " + elements[0].getClassName() + "." + elements[0].getMethodName() + "() line: " + elements[0].getLineNumber());
813 | String dump=obj_dump(cycleDayPicker);
814 | XposedBridge.log("HOOK ERROR createAdvDialog(): objDUMP: Dumping cycleDayPicker ...");
815 | for (String item : dump.split(System.getProperty("line.separator"))) {
816 | XposedBridge.log("HOOK ERROR createAdvDialog(): objDUMP: " + item);
817 | }
818 | }
819 |
820 | //Set layout cycleDaysPicker
821 | final NumberPicker cycleDaysPicker = (NumberPicker) view.findViewById(R_id_cycle_days);
822 | String[] values = new String[100];
823 | for (int i = 0; i < 100; ++i) {
824 | values[i] = "" + (i + 1);
825 | }
826 | values[0] = "1 - " + context.getString(modR_strings_nr1_daily);
827 | values[6] = "7 - " + context.getString(modR_strings_nr7_weekly);
828 | values[29] = "30 - " + context.getString(modR_strings_nr30_fixedmonth);
829 | values[30] = "31 - " + context.getString(modR_strings_nr31_monthly);
830 | cycleDaysPicker.setDisplayedValues(values);
831 | cycleDaysPicker.setMinValue(1);
832 | cycleDaysPicker.setMaxValue(100);
833 | cycleDaysPicker.setValue(pref_cycle_days);
834 | cycleDaysPicker.setWrapSelectorWheel(true);
835 | cycleDaysPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
836 |
837 | //Set layout cycleDatePicker
838 | final DatePicker cycleDatePicker = (DatePicker) view.findViewById(R_id_datepicker);
839 | int year = pref_cycle_date.get(Calendar.YEAR);
840 | int month = pref_cycle_date.get(Calendar.MONTH);
841 | int day = pref_cycle_date.get(Calendar.DAY_OF_MONTH);
842 | cycleDatePicker.updateDate(year, month, day);
843 | cycleDatePicker.setMaxDate(System.currentTimeMillis());
844 | cycleDatePicker.setDescendantFocusability(DatePicker.FOCUS_BLOCK_DESCENDANTS);
845 |
846 | // Set builder
847 | builder.setTitle("Advanced Cycle Editor"); //R.string.data_usage_cycle_editor_title
848 | builder.setView(view);
849 | if (Build.VERSION.SDK_INT == 23) {
850 | builder.setPositiveButton("OK", //R.string.data_usage_cycle_editor_positive
851 | new DialogInterface.OnClickListener() {
852 | @Override
853 | public void onClick(DialogInterface dialog, int which) {
854 | // clear focus to finish pending text edits
855 | XposedHelpers.callMethod(cycleDayPicker, "clearFocus");
856 | cycleDaysPicker.clearFocus();
857 | cycleDatePicker.clearFocus();
858 | int bs = encodeBitShiftedInt(cycleDatePicker, cycleDaysPicker);
859 |
860 | //Save in policy CycleDay
861 | final String cycleTimezone = new Time().timezone;
862 | XposedHelpers.callMethod(editor, "setPolicyCycleDay", template, bs, cycleTimezone);
863 | XposedHelpers.callMethod(target, "updatePolicy", true);
864 |
865 | }
866 | });
867 | } else if (Build.VERSION.SDK_INT == 24 || Build.VERSION.SDK_INT == 25) {
868 | XposedHelpers.setAdditionalStaticField(param.thisObject,"mCycleDatePicker",cycleDatePicker);
869 | XposedHelpers.setAdditionalStaticField(param.thisObject,"mCycleDaysPicker",cycleDaysPicker);
870 | builder.setPositiveButton("OK", (DialogInterface.OnClickListener) param.thisObject);
871 | }
872 |
873 | //Create Dialog & return
874 | if (DEBUG) XposedBridge.log("HOOK onCreateDialog(): Completed!");
875 | return builder.create();
876 | }
877 |
878 | //Encoding methods
879 | private static int encodeBitShiftedInt(DatePicker cycleDatePicker, NumberPicker cycleDaysPicker) {
880 | // Encode Day of Month, Month and Duration into one int
881 | // via BitShift method.
882 | int bs1 = cycleDatePicker.getDayOfMonth();
883 | int bs2 = cycleDatePicker.getMonth();
884 | int bs3 = cycleDaysPicker.getValue();
885 | int bs = (bs1 & 0xFF) | ((bs2 & 0xFF) << 8) | ((bs3 & 0xFF) << 16);
886 | if (DEBUG) XposedBridge.log("HOOK BITSH pref SAVED " + bs + " (" + bs1 + "." + bs2 + "." + bs3 + ")");
887 | return bs;
888 | }
889 |
890 | private static Object[] decodeBitShiftedInt(int cycleDay){
891 |
892 | // Get Preferences
893 | Calendar pref_cycleDate = Calendar.getInstance();
894 |
895 | //Set no hour
896 | pref_cycleDate.set(Calendar.HOUR_OF_DAY, 0);
897 | pref_cycleDate.set(Calendar.MINUTE, 0);
898 | pref_cycleDate.set(Calendar.SECOND, 0);
899 | pref_cycleDate.set(Calendar.MILLISECOND, 0);
900 |
901 | int pref_cycleDays = 31;
902 | if (DEBUG) XposedBridge.log("HOOK BITSH pref LOAD " + cycleDay + "");
903 | if(cycleDay <= 31){
904 | // Not Bitshifted
905 | pref_cycleDate.set(Calendar.DAY_OF_MONTH, cycleDay);
906 | } else {
907 | // Decode Day of Month, Month and Duration from one int
908 | // via BitShift method.
909 | try{
910 | int bs1 = cycleDay & 0xFF; // Day of Month
911 | int bs2 = (cycleDay >> 8) & 0xFF; // Month
912 | int bs3 = (cycleDay >> 16) & 0xFF; // num Days
913 | if(DEBUG) XposedBridge.log("HOOK BITSH loaded bitshited Ints "+bs1+"."+bs2+"."+bs3+"");
914 | pref_cycleDate.set(Calendar.DAY_OF_MONTH, bs1);
915 | pref_cycleDate.set(Calendar.MONTH, bs2);
916 | if(pref_cycleDate.getTimeInMillis() > System.currentTimeMillis()) {
917 | pref_cycleDate.set(Calendar.YEAR, pref_cycleDate.get(Calendar.YEAR) - 1);
918 | if(DEBUG) XposedBridge.log("HOOK BITSH preference year set to "+pref_cycleDate.get(Calendar.YEAR)+"");
919 | }
920 | if(bs3 != 0) pref_cycleDays = bs3; else { pref_cycleDays = 31; XposedBridge.log("HOOK ERR pref_cycleDays=0 - forced 31!"); }
921 | } catch (Exception ex){
922 | XposedBridge.log("HOOK ERROR decodeBitShiftedInt(): " + ex.getMessage());
923 | StackTraceElement[] elements = ex.getStackTrace();
924 | XposedBridge.log("HOOK ERROR decodeBitShiftedInt(): at " + elements[0].getClassName() + "." + elements[0].getMethodName() + "() line: " + elements[0].getLineNumber());
925 | }
926 | }
927 |
928 | return new Object[] {pref_cycleDate, pref_cycleDays};
929 | }
930 |
931 |
932 | //Status Bar methods
933 | private static Object getAdvDataUsageInfo(XC_MethodHook.MethodHookParam param, Class> networkTemplate, Class> networkStatsHistory, Class> dataUsageInfo) {
934 |
935 | //Get Session
936 | Object session = XposedHelpers.callMethod(param.thisObject, "getSession"); //INetworkStatsSession
937 | if (session == null) {
938 | return XposedHelpers.callMethod(param.thisObject, "warn", "no stats session");
939 | }
940 |
941 | //Get Template
942 | Object template = null;
943 | if(Build.VERSION.SDK_INT == 23) {
944 | final Context context = (Context) XposedHelpers.getObjectField(param.thisObject, "mContext");
945 | final String subscriberId = (String) XposedHelpers.callMethod(param.thisObject, "getActiveSubscriberId", context); //String
946 | if (subscriberId == null) {
947 | XposedHelpers.callMethod(param.thisObject, "warn", "no subscriber id");
948 | }
949 | if (DEBUG) XposedBridge.log("HOOK UI getAdvDataUsageInfo(): subscriberId :" + subscriberId + "");
950 | Object mTelephonyManager = XposedHelpers.getObjectField(param.thisObject, "mTelephonyManager");
951 | template = XposedHelpers.callStaticMethod(networkTemplate, "buildTemplateMobileAll", subscriberId);
952 | template = XposedHelpers.callStaticMethod(networkTemplate, "normalize", template, XposedHelpers.callMethod(mTelephonyManager, "getMergedSubscriberIds"));
953 | }
954 | else if(Build.VERSION.SDK_INT == 24 || Build.VERSION.SDK_INT == 25) {
955 | template = param.args[0];
956 | }
957 |
958 | final Object policy = XposedHelpers.callMethod(param.thisObject, "findNetworkPolicy", template);
959 | try {
960 | if(Build.VERSION.SDK_INT == 23) session = XposedHelpers.getObjectField(param.thisObject, "mSession");
961 |
962 | //Initialize vars
963 | int FIELD_RX_BYTES = XposedHelpers.getStaticIntField(networkStatsHistory, "FIELD_RX_BYTES");
964 | int FIELD_TX_BYTES = XposedHelpers.getStaticIntField(networkStatsHistory, "FIELD_TX_BYTES");
965 | int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES;
966 | final Object history = XposedHelpers.callMethod(session, "getHistoryForNetwork", template, FIELDS); //type NetworkStatsHistory
967 | final long now = System.currentTimeMillis();
968 | final long start, end;
969 |
970 | //Compute Cycle boundaries
971 | int cycleDay = 0;
972 | if (policy != null) { cycleDay = (int) XposedHelpers.getObjectField(policy, "cycleDay"); }
973 | if (policy != null && cycleDay > 31) {
974 | start = mComputeLastCycleBoundary(now, cycleDay);
975 | end = mComputeNextCycleBoundary(now, cycleDay);
976 | } else {
977 | // period = last 4 wks
978 | end = now;
979 | start = now - DateUtils.WEEK_IN_MILLIS * 4;
980 | }
981 |
982 | //Formatting and finalize values
983 | final long callStart = System.currentTimeMillis();
984 | final Object entry = XposedHelpers.callMethod(history, "getValues", start, end, now, null); //type NetworkStatsHistory.Entry
985 | final long callEnd = System.currentTimeMillis();
986 | if (DEBUG) XposedBridge.log("HOOK UI getAdvDataUsageInfo(): History call from " +
987 | new Date(start) + " to " + new Date(end) + " now=" + new Date(now) + " took " + (callEnd - callStart) + ": " +
988 | (String) XposedHelpers.callMethod(param.thisObject, "historyEntryToString", entry));
989 | if (entry == null) {
990 | if (DEBUG) XposedBridge.log("HOOK UI getAdvDataUsageInfo(): entry is null");
991 | return XposedHelpers.callMethod(param.thisObject, "warn", "no entry data");
992 | }
993 | final long totalBytes = XposedHelpers.getLongField(entry, "rxBytes") + XposedHelpers.getLongField(entry, "txBytes");
994 |
995 | //Create dataUsageInfo and return
996 | final Object usage = XposedHelpers.newInstance(dataUsageInfo);
997 | if(Build.VERSION.SDK_INT == 24 || Build.VERSION.SDK_INT == 25) { XposedHelpers.setLongField(usage, "startDate", (long) start); }
998 | XposedHelpers.setLongField(usage, "usageLevel", (long) totalBytes);
999 | XposedHelpers.setObjectField(usage, "period", (String) XposedHelpers.callMethod(param.thisObject, "formatDateRange", start, end));
1000 | if (policy != null) {
1001 | XposedHelpers.setLongField(usage, "limitLevel", XposedHelpers.getLongField(policy, "limitBytes") > 0 ? XposedHelpers.getLongField(policy, "limitBytes") : 0);
1002 | XposedHelpers.setLongField(usage, "warningLevel", XposedHelpers.getLongField(policy, "warningBytes") > 0 ? XposedHelpers.getLongField(policy, "warningBytes") : 0);
1003 | } else {
1004 | XposedHelpers.setLongField(usage, "warningLevel", 2L * 1024 * 1024 * 1024);
1005 | }
1006 | Object mNetworkController = XposedHelpers.getObjectField(param.thisObject, "mNetworkController");
1007 | if (usage != null && mNetworkController != null) {
1008 | XposedHelpers.setObjectField(usage, "carrier", (String) XposedHelpers.callMethod(mNetworkController, "getMobileDataNetworkName"));
1009 | }
1010 | if (DEBUG && usage != null)XposedBridge.log("HOOK UI getAdvDataUsageInfo(): usageLevel=" + XposedHelpers.getObjectField(usage, "usageLevel") + " period=" + XposedHelpers.getObjectField(usage, "period") + "");
1011 |
1012 | return usage;
1013 | } catch (Exception e) {
1014 | if (DEBUG) XposedBridge.log("HOOK UI getAdvDataUsageInfo(): remote call failed StackTrace:" + Log.getStackTraceString(e));
1015 | return XposedHelpers.callMethod(param.thisObject, "warn", "remote call failed");
1016 | }
1017 |
1018 | //Return Null
1019 | //if (DEBUG) XposedBridge.log("HOOK UI DataUsage: returning null!");
1020 | //return null;
1021 | }
1022 |
1023 |
1024 | /****************************
1025 | Utility Methods
1026 | */
1027 |
1028 | public static void view_dump(View view){
1029 | if(DEBUG) XposedBridge.log( "HOOK DUMP view " + view.toString());
1030 | try {
1031 | ViewGroup rootView = (ViewGroup) view.getRootView();
1032 | int childViewCount = rootView.getChildCount();
1033 | for (int i=0; i dayOne.get(Calendar.YEAR)) {
1065 | //swap them
1066 | Calendar temp = dayOne;
1067 | dayOne = dayTwo;
1068 | dayTwo = temp;
1069 | }
1070 | int extraDays = 0;
1071 |
1072 | int dayOneOriginalYearDays = dayOne.get(Calendar.DAY_OF_YEAR);
1073 |
1074 | while (dayOne.get(Calendar.YEAR) > dayTwo.get(Calendar.YEAR)) {
1075 | dayOne.add(Calendar.YEAR, -1);
1076 | // getActualMaximum() important for leap years
1077 | extraDays += dayOne.getActualMaximum(Calendar.DAY_OF_YEAR);
1078 | }
1079 |
1080 | return extraDays - dayTwo.get(Calendar.DAY_OF_YEAR) + dayOneOriginalYearDays ;
1081 | }
1082 | }
1083 |
1084 | public static String obj_dump(Object obj) {
1085 | StringBuilder result = new StringBuilder();
1086 | String newLine = System.getProperty("line.separator");
1087 |
1088 | result.append( obj.getClass().getName() );
1089 | result.append( " Object {" );
1090 | result.append(newLine);
1091 |
1092 | try {
1093 | //Fields
1094 | Field[] fields = obj.getClass().getDeclaredFields();
1095 | for ( Field field : fields ) {
1096 | result.append(" ");
1097 | try {
1098 | result.append( " (" + field.getType().getName() + ") " + field.getName() );
1099 | try {
1100 | //requires access to private field:
1101 | result.append( " = " + field.get(obj) );
1102 | } catch ( Throwable ex ) {
1103 | }
1104 | } catch ( Throwable ex ) {
1105 | result.append("Error dumping fields "+obj.getClass().getName()+":" + ex + "");
1106 | }
1107 | result.append(newLine);
1108 | }
1109 | //Methods
1110 | /*Method[] methods = obj.getClass().getDeclaredMethods();
1111 | for ( Method method : methods ) {
1112 | try {
1113 | result.append( method.getName() + " ");
1114 | Class retType = method.getReturnType();
1115 | Class[] paramTypes = method.getParameterTypes();
1116 | String name = method.getName();
1117 | result.append(" " + Modifier.toString(method.getModifiers()) + " " + retType.getName() + " " + name + "(");
1118 | for (int j = 0; j < paramTypes.length; j++) {
1119 | if (j > 0)
1120 | result.append(", ");
1121 | result.append(paramTypes[j].getName());
1122 | }
1123 | result.append(");");
1124 | } catch ( Throwable ex ) {
1125 | XposedBridge.log("HOOK error dumping methods "+obj.getClass().getName()+":" + ex + "");
1126 | }
1127 | result.append(newLine);
1128 | }*/
1129 | //Inner Methods
1130 | for (Class c = obj.getClass(); c != null; c = c.getSuperclass()) {
1131 | for (Method method : c.getDeclaredMethods()) {
1132 | try {
1133 | if (c!=obj.getClass()) result.append( c.getName() + "$");
1134 | Class retType = method.getReturnType();
1135 | Class[] paramTypes = method.getParameterTypes();
1136 | String name = method.getName();
1137 | result.append(" " + Modifier.toString(method.getModifiers()) + " " + retType.getName() + " " + name + "(");
1138 | for (int j = 0; j < paramTypes.length; j++) {
1139 | if (j > 0)
1140 | result.append(", ");
1141 | result.append(paramTypes[j].getName());
1142 | }
1143 | result.append(");");
1144 | } catch ( Throwable ex ) {
1145 | result.append("Error dumping inner methods "+obj.getClass().getName()+":" + ex + "");
1146 | }
1147 | result.append(newLine);
1148 | }
1149 | }
1150 | } catch (Throwable e) {
1151 | result.append("Error dumping obj "+obj.getClass().getName()+":" + e + "");
1152 | }
1153 |
1154 | result.append("}");
1155 |
1156 | return result.toString();
1157 | }
1158 |
1159 | }
1160 |
1161 |
1162 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m0m4x/AdvDataPlan/9d5f81d34666f5c86fb550420a5677a1bc137d4c/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m0m4x/AdvDataPlan/9d5f81d34666f5c86fb550420a5677a1bc137d4c/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m0m4x/AdvDataPlan/9d5f81d34666f5c86fb550420a5677a1bc137d4c/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m0m4x/AdvDataPlan/9d5f81d34666f5c86fb550420a5677a1bc137d4c/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m0m4x/AdvDataPlan/9d5f81d34666f5c86fb550420a5677a1bc137d4c/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m0m4x/AdvDataPlan/9d5f81d34666f5c86fb550420a5677a1bc137d4c/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m0m4x/AdvDataPlan/9d5f81d34666f5c86fb550420a5677a1bc137d4c/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m0m4x/AdvDataPlan/9d5f81d34666f5c86fb550420a5677a1bc137d4c/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m0m4x/AdvDataPlan/9d5f81d34666f5c86fb550420a5677a1bc137d4c/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m0m4x/AdvDataPlan/9d5f81d34666f5c86fb550420a5677a1bc137d4c/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values-it/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Durata in giorni del piano:
4 | Giornaliero
5 | Settimanale
6 | Mensile corto
7 | Mensile intero
8 | Giorno di inizio del piano:
9 | Ogni %1$s giorni
10 | a partire dal %1$s
11 | di %1$s giorni
12 | Il ciclo è iniziato in data %1$s e finirà in data %2$s alle ore 00:00. La durata del ciclo è %3$s.
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Advanced Data Plan
3 | Data Plan days:
4 | Day to start data usage cycle:
5 | Daily
6 | Weekly
7 | Fixed Monthly
8 | Monthly
9 | Every %1$s days
10 | starting %1$s
11 | %1$s days
12 | The cycle started on %1$s and will ends on %2$s at 00:00. The cycle duration is %3$s.
13 |
14 |
--------------------------------------------------------------------------------
/app/trash.txt:
--------------------------------------------------------------------------------
1 | /*
2 | findAndHookMethod("com.android.settings.DataUsageSummary.CycleEditorFragment", lpparam.classLoader, "onCreateDialog", "android.os.Bundle" , new XC_MethodHook() {
3 |
4 | @Override
5 | protected void beforeHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {
6 | // this will be called before the clock was updated by the original method
7 | XposedBridge.log("HOOK before onCreateDialog CycleEditorFragment !");
8 |
9 |
10 | }
11 | @RequiresApi(api = Build.VERSION_CODES.M)
12 | @Override
13 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
14 | // this will be called after the clock was updated by the original method
15 | XposedBridge.log("HOOK after onCreateDialog CycleEditorFragment !");
16 | DialogFragment mCycleEditorFragment = (DialogFragment) param.thisObject; //CycleEditorFragment
17 |
18 | View viewDialog = (View) XposedHelpers.callMethod(mCycleEditorFragment,"getView"); //getView();
19 | final Context context = mCycleEditorFragment.getContext(); //SubSettings
20 |
21 | Fragment target = mCycleEditorFragment.getTargetFragment();
22 | Object editor = XposedHelpers.getObjectField(target, "mPolicyEditor"); //type NetworkPolicyEditor
23 | Bundle bundle = (Bundle) (XposedHelpers.callMethod(mCycleEditorFragment, "getArguments"));
24 | Object template = bundle.getParcelable("template"); //type NetworkTemplate
25 | int cycleDay = (int) XposedHelpers.callMethod(editor, "getPolicyCycleDay", template);
26 |
27 | XposedBridge.log("HOOK cycleDay - " + cycleDay + " !");
28 |
29 | //Salva valori
30 | //XposedHelpers.setAdditionalStaticField(mCycleEditorFragment,"","");
31 |
32 | final AlertDialog.Builder builder = new AlertDialog.Builder(context);
33 | final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
34 | final View view = dialogInflater.inflate(R.layout.testlayout, null, false);
35 | builder.setView(view);
36 | builder.create();
37 |
38 | final DatePicker mDatePicker1 = (DatePicker) XposedHelpers.callMethod( view, "findViewById" , R.id.datePicker );
39 | mDatePicker1.updateDate(2017,6,21);
40 |
41 |
42 |
43 |
44 |
45 | /*DatePicker mDatePicker = (DatePicker) XposedHelpers.getObjectField(
46 | viewSubSettings, "datePicker");
47 | NumberPicker mNumberPicker = (NumberPicker) XposedHelpers.getObjectField(
48 | viewSubSettings, "cycle_days");
49 | NumberPicker defNumberPicker = (NumberPicker) XposedHelpers.getObjectField(
50 | viewSubSettings, "cycle_day");*/
51 | /*int monthDay = defNumberPicker.getValue();
52 | Calendar today = Calendar.getInstance();
53 | today.set(Calendar.DAY_OF_MONTH, monthDay);
54 | int year=today.get(Calendar.YEAR);
55 | int month=today.get(Calendar.MONTH);
56 | int day=today.get(Calendar.DAY_OF_MONTH);
57 | mDatePicker.updateDate(year, month, day);*/
58 |
59 | //final Class DataUsageSuammary = XposedHelpers.findClass("com.android.settings.DataUsageSummary", lpparam.classLoader);
60 | /*
61 | mNumberPicker.setMinValue(1);
62 | mNumberPicker.setMaxValue(31);
63 | mNumberPicker.setValue(1);
64 | mNumberPicker.setWrapSelectorWheel(true);* /
65 | }
66 | });
67 | */
--------------------------------------------------------------------------------
/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 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.3.3'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m0m4x/AdvDataPlan/9d5f81d34666f5c86fb550420a5677a1bc137d4c/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Apr 09 12:52:47 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/screenshot/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m0m4x/AdvDataPlan/9d5f81d34666f5c86fb550420a5677a1bc137d4c/screenshot/1.png
--------------------------------------------------------------------------------
/screenshot/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m0m4x/AdvDataPlan/9d5f81d34666f5c86fb550420a5677a1bc137d4c/screenshot/2.png
--------------------------------------------------------------------------------
/screenshot/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m0m4x/AdvDataPlan/9d5f81d34666f5c86fb550420a5677a1bc137d4c/screenshot/3.png
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------