diff --git a/.gitignore b/.gitignore index d60ba7d..7c39332 100644 --- a/.gitignore +++ b/.gitignore @@ -1,31 +1,27 @@ + # built application files *.apk *.ap_ - # files for the dex VM *.dex -*.txt - # Java class files *.class - # generated files bin/ gen/ -lib/ - +out/ +build/ +.gradle/ +# Project files +*.iml +.idea +# .idea/workspace.xml # Local configuration file (sdk path, etc) local.properties - -# Eclipse project files -.classpath -.project - -# Proguard folder generated by Eclipse -proguard/ - -# Intellij project files -*.iml -*.ipr +keystore.properties +# Windows thumbnail db +.DS_Store +# Idea non-crucial project fileS *.iws -.idea/ +# Sandbox stuff + diff --git a/LICENSE b/LICENSE deleted file mode 100644 index e719e8e..0000000 --- a/LICENSE +++ /dev/null @@ -1,17 +0,0 @@ - -Performance Tuner - -Copyright (c) 2013 Rahul Kumar aka PhantomLord - - -Performance Tuner is free software: you can redistribute it and/or modify it under the -terms of the GNU General Public License as published by the Free Software -Foundation, either version 3 of the License, or (at your option) any later -version. - - Performance Tuner is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A -PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along -with Performance Tuner. If not, see . diff --git a/README.md b/README.md deleted file mode 100644 index 627bf38..0000000 --- a/README.md +++ /dev/null @@ -1,39 +0,0 @@ -Performance-Tweaker - -======================= - -(c) 2014 Rahul Kumar - -Simple Android App for - -->overclocking/underclocking the cpu clocks and tweaking various other kernel parameters like I/O , Governor etc. - -->Monitoring the time spent by a cpu state in each freqeuncy as well as current kernel information. - -->Spot rogue applications or sources which are causing battery drain i.e Battery and Wakelock Stats (Work In Progress). - - -To Download the App - -http://forum.xda-developers.com/devdb/project/dl/?id=7519&task=get - - -Third party libraries: - -* Sherlock-navigation-Drawer -* Actionbar Sherlock -* Android-Common (https://github.com/asksven/AndroidCommon) -* Spinner-Wheel -* BugSense (For analytics and stuff) - -There is also a discussion thread on XDA Developers: - -http://forum.xda-developers.com/showthread.php?t=2728587 - - -=== LICENSE === - -See the LICENSE file. - - - diff --git a/androidCommon/build.gradle b/androidCommon/build.gradle new file mode 100644 index 0000000..0f10f46 --- /dev/null +++ b/androidCommon/build.gradle @@ -0,0 +1,24 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 21 + buildToolsVersion "21.0.1" + + defaultConfig { + + minSdkVersion 8 + targetSdkVersion 18 + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + } + } +} + +dependencies { + compile 'com.google.code.gson:gson:2.2.2' + compile files('libs/RootTools-2.6.jar') +} diff --git a/androidCommon/libs/RootTools-2.6.jar b/androidCommon/libs/RootTools-2.6.jar new file mode 100644 index 0000000..78ffca9 Binary files /dev/null and b/androidCommon/libs/RootTools-2.6.jar differ diff --git a/androidCommon/lint.xml b/androidCommon/lint.xml new file mode 100644 index 0000000..8a0094b --- /dev/null +++ b/androidCommon/lint.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/androidCommon/src/main/AndroidManifest.xml b/androidCommon/src/main/AndroidManifest.xml new file mode 100644 index 0000000..2e96601 --- /dev/null +++ b/androidCommon/src/main/AndroidManifest.xml @@ -0,0 +1,16 @@ + + + + + + + + + + \ No newline at end of file diff --git a/androidCommon/src/main/java/com/asksven/andoid/common/contrib/Debug.java b/androidCommon/src/main/java/com/asksven/andoid/common/contrib/Debug.java new file mode 100644 index 0000000..a4f1ab1 --- /dev/null +++ b/androidCommon/src/main/java/com/asksven/andoid/common/contrib/Debug.java @@ -0,0 +1,40 @@ +/** + * Contrib by Chainfire (see https://raw.github.com/Chainfire/libsuperuser/master/libsuperuser/src/eu/chainfire/libsuperuser/Shell.java) + */ +package com.asksven.andoid.common.contrib; + +/* + * Copyright (C) 2012 Jorrit "Chainfire" Jongma + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.asksven.android.common.CommonLogSettings; + +import android.util.Log; + +/** + * Utility class that intentionally does nothing when not in debug mode + */ +public class Debug { + /** + * Log a message if we are in debug mode + * @param message The message to log + */ + public static void log(String message) { + if (CommonLogSettings.DEBUG) + { + Log.d("libsuperuser", "[libsuperuser]" + (!message.startsWith("[") && !message.startsWith(" ") ? " " : "") + message); + } + } +} diff --git a/androidCommon/src/main/java/com/asksven/andoid/common/contrib/Shell.java b/androidCommon/src/main/java/com/asksven/andoid/common/contrib/Shell.java new file mode 100644 index 0000000..276f8d5 --- /dev/null +++ b/androidCommon/src/main/java/com/asksven/andoid/common/contrib/Shell.java @@ -0,0 +1,266 @@ +///** +// * Contrib by Chainfire (see https://raw.github.com/Chainfire/libsuperuser/master/libsuperuser/src/eu/chainfire/libsuperuser/Shell.java) +// */ +// +///* +// * Copyright (C) 2012 Jorrit "Chainfire" Jongma +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +// +//package com.asksven.andoid.common.contrib; +// +//import java.io.DataOutputStream; +//import java.io.IOException; +//import java.util.ArrayList; +//import java.util.Collections; +//import java.util.List; +// +//import com.asksven.android.common.privateapiproxies.BuildConfig; +// +//import android.os.Looper; +// +///** +// * Class providing functionality to execute commands in a (root) shell +// */ +//public class Shell { +// /** +// * Runs commands using the supplied shell, and returns the output, or null in +// * case of errors. +// * +// * Note that due to compatibility with older Android versions, +// * wantSTDERR is not implemented using redirectErrorStream, but rather appended +// * to the output. STDOUT and STDERR are thus not guaranteed to be in the correct +// * order in the output. +// * +// * Note as well that this code will intentionally crash when run in debug mode +// * from the main thread of the application. You should always execute shell +// * commands from a background thread. +// * +// * When in debug mode, the code will also excessively log the commands passed to +// * and the output returned from the shell. +// * +// * Though this function uses background threads to gobble STDOUT and STDERR so +// * a deadlock does not occur if the shell produces massive output, the output is +// * still stored in a List, and as such doing something like "ls -lR /" +// * will probably have you run out of memory. +// * +// * @param shell The shell to use for executing the commands +// * @param commands The commands to execute +// * @param wantSTDERR Return STDERR in the output ? +// * @return Output of the commands, or null in case of an error +// */ +// public static List run(String shell, String[] commands, boolean wantSTDERR) { +// String shellUpper = shell.toUpperCase(); +// +//// if (BuildConfig.DEBUG) { +//// // check if we're running in the main thread, and if so, crash if we're in debug mode, +//// // to let the developer know attention is needed here. +//// +//// if (Looper.myLooper() == Looper.getMainLooper()) { +//// Debug.log("Application attempted to run a shell command from the main thread"); +//// throw new ShellOnMainThreadException(); +//// } +//// +//// Debug.log(String.format("[%s%%] START", shellUpper)); +//// } +// +// List res = Collections.synchronizedList(new ArrayList()); +// +// try { +// // setup our process, retrieve STDIN stream, and STDOUT/STDERR gobblers +// Process process = Runtime.getRuntime().exec(shell); +// DataOutputStream STDIN = new DataOutputStream(process.getOutputStream()); +// StreamGobbler STDOUT = new StreamGobbler(shellUpper + "-", process.getInputStream(), res); +// StreamGobbler STDERR = new StreamGobbler(shellUpper + "*", process.getErrorStream(), wantSTDERR ? res : null); +// +// // start gobbling and write our commands to the shell +// STDOUT.start(); +// STDERR.start(); +// for (String write : commands) { +// if (BuildConfig.DEBUG) Debug.log(String.format("[%s+] %s", shellUpper, write)); +// STDIN.writeBytes(write + "\n"); +// STDIN.flush(); +// } +// STDIN.writeBytes("exit\n"); +// STDIN.flush(); +// +// // wait for our process to finish, while we gobble away in the background +// process.waitFor(); +// +// // make sure our threads are done gobbling, our streams are closed, and the process is +// // destroyed - while the latter two shouldn't be needed in theory, and may even produce +// // warnings, in "normal" Java they are required for guaranteed cleanup of resources, so +// // lets be safe and do this on Android as well +// try { +// STDIN.close(); +// } catch (IOException e) { +// } +// STDOUT.join(); +// STDERR.join(); +// process.destroy(); +// +// // in case of su, 255 usually indicates access denied +// if (shell.equals("su") && (process.exitValue() == 255)) { +// res = null; +// } +// } catch (IOException e) { +// // shell probably not found +// res = null; +// } catch (InterruptedException e) { +// // this should really be re-thrown +// res = null; +// } +// +// if (BuildConfig.DEBUG) Debug.log(String.format("[%s%%] END", shell.toUpperCase())); +// return res; +// } +// +// /** +// * This class provides utility functions to easily execute commands using SH +// */ +// public static class SH { +// /** +// * Runs command and return output +// * +// * @param command The command to run +// * @return Output of the command, or null in case of an error +// */ +// public static List run(String command) { +// return Shell.run("sh", new String[] { command }, false); +// } +// +// /** +// * Runs commands and return output +// * +// * @param commands The commands to run +// * @return Output of the commands, or null in case of an error +// */ +// public static List run(List commands) { +// return Shell.run("sh", commands.toArray(new String[commands.size()]), false); +// } +// +// /** +// * Runs command and return output +// * +// * @param commands The commands to run +// * @return Output of the commands, or null in case of an error +// */ +// public static List run(String[] commands) { +// return Shell.run("sh", commands, false); +// } +// } +// +// /** +// * This class provides utility functions to easily execute commands using SU +// * (root shell), as well as detecting whether or not root is available, and +// * if so which version. +// */ +// public static class SU { +// /** +// * Runs command as root (if available) and return output +// * +// * @param command The command to run +// * @return Output of the command, or null if root isn't available or in case of an error +// */ +// public static List run(String command) { +// return Shell.run("su", new String[] { command }, false); +// } +// +// /** +// * Runs commands as root (if available) and return output +// * +// * @param command The commands to run +// * @return Output of the commands, or null if root isn't available or in case of an error +// */ +// public static List run(List commands) { +// return Shell.run("su", commands.toArray(new String[commands.size()]), false); +// } +// +// /** +// * Runs commands as root (if available) and return output +// * +// * @param command The commands to run +// * @return Output of the commands, or null if root isn't available or in case of an error +// */ +// public static List run(String[] commands) { +// return Shell.run("su", commands, false); +// } +// +// /** +// * Detects whether or not superuser access is available, by checking the output +// * of the "id" command if available, checking if a shell runs at all otherwise +// * +// * @return True if superuser access available +// */ +// public static boolean available() { +// // this is only one of many ways this can be done +// +// List ret = run(new String[] { +// "id", +// "echo -EOC-" +// }); +// if (ret == null) return false; +// +// for (String line : ret) { +// if (line.contains("uid=")) { +// // id command is working, let's see if we are actually root +// return line.contains("uid=0"); +// } else if (line.contains("-EOC-")) { +// // if we end up here, the id command isn't present, but at +// // least the su commands starts some kind of shell, let's +// // hope it has root priviliges - no way to know without +// // additional native binaries +// return true; +// } +// } +// return false; +// } +// +// /** +// * Detects the version of the su binary installed (if any), if supported by the binary. +// * Most binaries support two different version numbers, the public version that is +// * displayed to users, and an internal version number that is used for version number +// * comparisons. Returns null if su not available or retrieving the version isn't supported. +// * +// * Note that su binary version and GUI (APK) version can be completely different. +// * +// * @param internal Request human-readable version or application internal version +// * @return String containing the su version or null +// */ +// public static String version(boolean internal) { +// // we add an additional exit call, because the command +// // line options are not available in all su versions, +// // thus potentially launching a shell instead +// +// List ret = Shell.run("sh", new String[] { +// internal ? "su -V" : "su -v", +// "exit" +// }, false); +// if (ret == null) return null; +// +// for (String line : ret) { +// if (!internal) { +// if (line.contains(".")) return line; +// } else { +// try { +// if (Integer.parseInt(line) > 0) return line; +// } catch(NumberFormatException e) { +// } +// } +// } +// return null; +// } +// } +//} +// diff --git a/androidCommon/src/main/java/com/asksven/andoid/common/contrib/ShellOnMainThreadException.java b/androidCommon/src/main/java/com/asksven/andoid/common/contrib/ShellOnMainThreadException.java new file mode 100644 index 0000000..21b0fab --- /dev/null +++ b/androidCommon/src/main/java/com/asksven/andoid/common/contrib/ShellOnMainThreadException.java @@ -0,0 +1,15 @@ +/** + * Contrib by Chainfire (see https://raw.github.com/Chainfire/libsuperuser/master/libsuperuser/src/eu/chainfire/libsuperuser/Shell.java) + */ +package com.asksven.andoid.common.contrib; + +/** + * Exception class used to crash application when shell commands are executed + * from the main thread, and we are in debug mode. + */ +@SuppressWarnings("serial") +public class ShellOnMainThreadException extends RuntimeException { + public ShellOnMainThreadException() { + super("Application attempted to run a shell command from the main thread"); + } +} diff --git a/androidCommon/src/main/java/com/asksven/andoid/common/contrib/StreamGobbler.java b/androidCommon/src/main/java/com/asksven/andoid/common/contrib/StreamGobbler.java new file mode 100644 index 0000000..4e9e7b3 --- /dev/null +++ b/androidCommon/src/main/java/com/asksven/andoid/common/contrib/StreamGobbler.java @@ -0,0 +1,78 @@ +/** + * Contrib by Chainfire (see https://raw.github.com/Chainfire/libsuperuser/master/libsuperuser/src/eu/chainfire/libsuperuser/Shell.java) + */ + +/* + * Copyright (C) 2012 Jorrit "Chainfire" Jongma + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.asksven.andoid.common.contrib; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.List; + +import com.asksven.android.common.privateapiproxies.BuildConfig; + +/** + * Thread utility class continuously reading from an InputStream + */ +public class StreamGobbler extends Thread { + private String shell = null; + private BufferedReader reader = null; + private List writer = null; + + /** + * StreamGobbler constructor + * + * We use this class because shell STDOUT and STDERR should be read as quickly as + * possible to prevent a deadlock from occurring, or Process.waitFor() never + * returning (as the buffer is full, pausing the native process + * + * @param shell Name of the shell + * @param inputStream InputStream to read from + * @param outputList List to write to, or null + */ + public StreamGobbler(String shell, InputStream inputStream, List outputList) { + this.shell = shell; + reader = new BufferedReader(new InputStreamReader(inputStream)); + writer = outputList; + } + + @Override + public void run() { + // keep reading the InputStream until it ends (or an error occurs) + try { + String line = null; + while ((line = reader.readLine()) != null) { + if (BuildConfig.DEBUG) { + Debug.log(String.format("[%s] %s", shell, line)); + } + if (writer != null) { + writer.add(line); + } + } + } catch (IOException e) { + } + + // make sure our stream is closed and resources will be freed + try { + reader.close(); + } catch (IOException e) { + } + } +} diff --git a/androidCommon/src/main/java/com/asksven/andoid/common/contrib/Util.java b/androidCommon/src/main/java/com/asksven/andoid/common/contrib/Util.java new file mode 100644 index 0000000..da2ecbc --- /dev/null +++ b/androidCommon/src/main/java/com/asksven/andoid/common/contrib/Util.java @@ -0,0 +1,162 @@ +/******************************************************************************* + * Copyright (c) 2011 Adam Shanks (ChainsDD) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package com.asksven.andoid.common.contrib; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.preference.PreferenceManager; +import android.text.format.DateFormat; +import android.util.Log; +import android.util.SparseArray; + + +public class Util { + private static final String TAG = "Util"; + + + + + public static ArrayList run(String command) { + return run("/system/bin/sh", command); + } + + public static ArrayList run(String shell, String command) { + return run(shell, new String[] { + command + }); + } + + public static ArrayList run(String shell, ArrayList commands) { + String[] commandsArray = new String[commands.size()]; + commands.toArray(commandsArray); + return run(shell, commandsArray); + } + + public static ArrayList run(String shell, String[] commands) { + ArrayList output = new ArrayList(); + + try { + Process process = Runtime.getRuntime().exec(shell); + + BufferedOutputStream shellInput = + new BufferedOutputStream(process.getOutputStream()); + BufferedReader shellOutput = + new BufferedReader(new InputStreamReader(process.getInputStream())); + + for (String command : commands) { + Log.i(TAG, "command: " + command); + shellInput.write((command + " 2>&1\n").getBytes()); + } + + shellInput.write("exit\n".getBytes()); + shellInput.flush(); + + String line; + while ((line = shellOutput.readLine()) != null) { +// Log.d(TAG, "command output: " + line); + output.add(line); + } + + process.waitFor(); + } catch (IOException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + return output; + } + + public static boolean writeStoreFile(Context context, int uid, int execUid, String cmd, int allow) { + File storedDir = new File(context.getFilesDir().getAbsolutePath() + File.separator + "stored"); + storedDir.mkdirs(); + if (cmd == null) { + Log.d(TAG, "App stored for logging purposes, file not required"); + return false; + } + String fileName = uid + "-" + execUid; + try { + OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream( + new File(storedDir.getAbsolutePath() + File.separator + fileName))); + out.write(cmd); + out.write('\n'); + out.write(String.valueOf(allow)); + out.write('\n'); + out.flush(); + out.close(); + } catch (FileNotFoundException e) { + Log.w(TAG, "Store file not written", e); + return false; + } catch (IOException e) { + Log.w(TAG, "Store file not written", e); + return false; + } + return true; + } + + public static boolean writeDetaultStoreFile(Context context, String action) { + File storedDir = new File(context.getFilesDir().getAbsolutePath() + File.separator + "stored"); + storedDir.mkdirs(); + File defFile = new File(storedDir.getAbsolutePath() + File.separator + "default"); + try { + OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(defFile.getAbsolutePath())); + if (action.equals("allow")) { + out.write("1"); + } else if (action.equals("deny")) { + out.write("0"); + } else { + out.write("-1"); + } + out.flush(); + out.close(); + } catch (FileNotFoundException e) { + Log.w(TAG, "Default file not written", e); + return false; + } catch (IOException e) { + Log.w(TAG, "Default file not written", e); + return false; + } + return true; + } +} diff --git a/androidCommon/src/main/java/com/asksven/android/common/AppRater.java b/androidCommon/src/main/java/com/asksven/android/common/AppRater.java new file mode 100644 index 0000000..b79afc5 --- /dev/null +++ b/androidCommon/src/main/java/com/asksven/android/common/AppRater.java @@ -0,0 +1,136 @@ +/** + * + */ +package com.asksven.android.common; + +import com.asksven.android.common.privateapiproxies.R; + +import android.app.Dialog; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.Uri; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + +/** + * @author sven From + * http://www.androidsnippets.com/prompt-engaged-users-to-rate- + * your-app-in-the-android-market-appirater + * Make sure to add following to strings.xml + * AndroidCommon + * com.asksven.androidcommon + * Remind me later + * Rate + * No, thanks + * If you enjoy using %s, please take a moment to rate it. Thanks for your support! + * + * To test it and to tweak the dialog appearence, you can call AppRater.showRateDialog(this, null) + * from your Activity. + * + * Normal use is to invoke AppRater.app_launched(this) each time your activity is invoked + * (eg. from within the onCreate method). If all conditions are met, the dialog appears. + */ +public class AppRater +{ + private final static int DAYS_UNTIL_PROMPT = 3; + private final static int LAUNCHES_UNTIL_PROMPT = 7; + + public static void app_launched(Context ctx) + { + SharedPreferences prefs = ctx.getSharedPreferences("apprater", 0); + if (prefs.getBoolean("dontshowagain", false)) + { + return; + } + + SharedPreferences.Editor editor = prefs.edit(); + + // Increment launch counter + long launch_count = prefs.getLong("launch_count", 0) + 1; + editor.putLong("launch_count", launch_count); + + // Get date of first launch + Long date_firstLaunch = prefs.getLong("date_firstlaunch", 0); + if (date_firstLaunch == 0) + { + date_firstLaunch = System.currentTimeMillis(); + editor.putLong("date_firstlaunch", date_firstLaunch); + } + + // Wait at least n days before opening + if (launch_count >= LAUNCHES_UNTIL_PROMPT) + { + if (System.currentTimeMillis() >= date_firstLaunch + (DAYS_UNTIL_PROMPT * 24 * 60 * 60 * 1000)) + { + showRateDialog(ctx, editor); + } + } + + editor.commit(); + } + + public static void showRateDialog(final Context ctx, final SharedPreferences.Editor editor) + { + final Dialog dialog = new Dialog(ctx); + dialog.setTitle(ctx.getString(R.string.label_button_rate) + " " + ctx.getString(R.string.app_name)); + + LinearLayout ll = new LinearLayout(ctx); + ll.setOrientation(LinearLayout.VERTICAL); + + TextView tv = new TextView(ctx); + tv.setText(ctx.getString(R.string.text_dialog_rate, ctx.getString(R.string.app_name))); + tv.setWidth(240); + tv.setPadding(4, 0, 4, 10); + ll.addView(tv); + + Button b1 = new Button(ctx); + b1.setText(ctx.getString(R.string.label_button_rate)); + b1.setOnClickListener(new Button.OnClickListener() + { + public void onClick(View v) + { + ctx.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + ctx.getString(R.string.app_pname)))); + if (editor != null) + { + editor.putBoolean("dontshowagain", true); + editor.commit(); + } + dialog.dismiss(); + } + }); + ll.addView(b1); + + Button b2 = new Button(ctx); + b2.setText(R.string.label_button_remind); + b2.setOnClickListener(new Button.OnClickListener() + { + public void onClick(View v) + { + dialog.dismiss(); + } + }); + ll.addView(b2); + + Button b3 = new Button(ctx); + b3.setText(R.string.label_button_no); + b3.setOnClickListener(new Button.OnClickListener() + { + public void onClick(View v) + { + if (editor != null) + { + editor.putBoolean("dontshowagain", true); + editor.commit(); + } + dialog.dismiss(); + } + }); + ll.addView(b3); + + dialog.setContentView(ll); + dialog.show(); + } +} diff --git a/androidCommon/src/main/java/com/asksven/android/common/CommonLogSettings.java b/androidCommon/src/main/java/com/asksven/android/common/CommonLogSettings.java new file mode 100644 index 0000000..2f227dd --- /dev/null +++ b/androidCommon/src/main/java/com/asksven/android/common/CommonLogSettings.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2011 asksven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.asksven.android.common; + +/** + * @author sven + * + */ +public class CommonLogSettings +{ + public static final String LOGGING_TAG = "AndoidCommon"; + public static boolean DEBUG = false; + public static boolean TRACE = false; + +} diff --git a/androidCommon/src/main/java/com/asksven/android/common/ReadmeActivity.java b/androidCommon/src/main/java/com/asksven/android/common/ReadmeActivity.java new file mode 100644 index 0000000..0ac6bab --- /dev/null +++ b/androidCommon/src/main/java/com/asksven/android/common/ReadmeActivity.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2011 asksven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.asksven.android.common; + + +import com.asksven.android.common.privateapiproxies.R; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.view.View; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.widget.Button; + +/** + * + * @author sven + * + * Add following strings to your app + * Other Apps + * Dismiss + * Follow me + * https://twitter.com/#!/asksven + * market://search?q=com.asksven + * + * and the activity to your manifest + * + */ +public class ReadmeActivity extends Activity +{ + /** + * @see android.app.Activity#onCreate(Bundle) + */ + @Override + protected void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + setContentView(R.layout.readmewebview); + + WebView browser = (WebView)findViewById(R.id.webview); + + WebSettings settings = browser.getSettings(); + settings.setJavaScriptEnabled(true); + + // retrieve any passed data (filename) + String strFilename = getIntent().getStringExtra("filename"); + String strURL = getIntent().getStringExtra("url"); + + // if a URL is passed open it + // if not open a local file + if ( (strURL == null) || (strURL.equals("")) ) + { + if (strFilename.equals("")) + { + browser.loadUrl("file:///android_asset/help.html"); + } + else + { + browser.loadUrl("file:///android_asset/" + strFilename); + } + } + else + { + browser.loadUrl(strURL); + } + + final Button buttonClose = (Button) findViewById(R.id.buttonClose); + buttonClose.setOnClickListener(new View.OnClickListener() + { + public void onClick(View v) + { + finish(); + } + }); + + final Button buttonRate = (Button) findViewById(R.id.buttonMarket); + buttonRate.setOnClickListener(new View.OnClickListener() + { + public void onClick(View v) + { + openURL(ReadmeActivity.this.getString(R.string.market_link)); + finish(); + } + }); + final Button buttonFollow = (Button) findViewById(R.id.buttonTwitter); + buttonFollow.setOnClickListener(new View.OnClickListener() + { + public void onClick(View v) + { + openURL(ReadmeActivity.this.getString(R.string.twitter_link)); + finish(); + } + }); + + } + + public void openURL( String inURL ) + { + Intent browse = new Intent( Intent.ACTION_VIEW , Uri.parse( inURL ) ); + + startActivity( browse ); + } + +} diff --git a/androidCommon/src/main/java/com/asksven/android/common/RootShell.java b/androidCommon/src/main/java/com/asksven/android/common/RootShell.java new file mode 100644 index 0000000..e942dd3 --- /dev/null +++ b/androidCommon/src/main/java/com/asksven/android/common/RootShell.java @@ -0,0 +1,90 @@ +/** + * + */ +package com.asksven.android.common; + +import java.util.ArrayList; +import java.util.List; + +//import com.asksven.andoid.common.contrib.Shell; +import com.stericson.RootTools.RootTools; +import com.stericson.RootTools.execution.Command; +import com.stericson.RootTools.execution.Shell; + +/** + * @author sven + * Sigleton performing su operations + * + */ +public class RootShell +{ + static RootShell m_instance = null; + static Shell m_shell = null; + private RootShell() + { + } + + public static RootShell getInstance() + { + if (m_instance == null) + { + m_instance = new RootShell(); + try + { + m_shell = RootTools.getShell(true); + } + catch (Exception e) + { + m_shell = null; + } + } + + return m_instance; + } + +// public List run1(String command) +// { +// return Shell.SU.run(command); +// } + + public synchronized List run(String command) + { + final List res = new ArrayList(); + + if (!RootTools.isRootAvailable()) + { + return res; + } + + if (m_shell == null) + { + // reopen if for whatever reason the shell got closed + RootShell.getInstance(); + } + Command shellCommand = new Command(0, command) + { + @Override + public void output(int id, String line) + { + res.add(line); + } + }; + try + { + RootTools.getShell(true).add(shellCommand).waitForFinish(); + } + catch (Exception e) + { + + } + + return res; + + } + + public boolean rooted() + { + return RootTools.isRootAvailable(); + } + +} diff --git a/androidCommon/src/main/java/com/asksven/android/common/kernelutils/AlarmDumpsysTests.java b/androidCommon/src/main/java/com/asksven/android/common/kernelutils/AlarmDumpsysTests.java new file mode 100644 index 0000000..401a06f --- /dev/null +++ b/androidCommon/src/main/java/com/asksven/android/common/kernelutils/AlarmDumpsysTests.java @@ -0,0 +1,64 @@ +/** + * + */ +package com.asksven.android.common.kernelutils; + +import java.util.ArrayList; +import java.util.List; + +import com.asksven.android.common.privateapiproxies.StatElement; + +import junit.framework.TestCase; + +/** + * @author sven + * + */ +public class AlarmDumpsysTests extends TestCase +{ + + /** + * Test method for {@link com.asksven.android.common.kernelutils.AlarmsDumpsys#getAlarms()}. + */ + public void testGetAlarms() + { + ArrayList test4_3 = AlarmsDumpsys.getAlarmsFrom_4_3(getTestData_4_3()); + assertNotNull(test4_3); + assertTrue(test4_3.size() > 1); + System.out.print(test4_3); + } + + static ArrayList getTestData_4_3() + { + ArrayList myRet = new ArrayList() + {{ + add("Alarm Stats:"); + add(" android +1m35s119ms running, 67 wakeups:"); + add(" +39s818ms 0 wakes 9 alarms: act=com.android.server.action.NETWORK_STATS_POLL"); + add(" +4s693ms 8 wakes 8 alarms: act=android.appwidget.action.APPWIDGET_UPDATE cmp={com.devexpert.weather/com.devexpert.weather.view.WidgetWeather5x2}"); + add(" com.google.android.gsf +5s50ms running, 30 wakeups:"); + add(" +5s50ms 30 wakes 30 alarms: cmp={com.google.android.gsf/com.google.android.gsf.checkin.EventLogService$Receiver}"); + add(" com.android.vending +46ms running, 4 wakeups:"); + add(" +29ms 1 wakes 1 alarms: cmp={com.android.vending/com.google.android.finsky.services.DailyHygiene}"); + add(" +17ms 3 wakes 3 alarms: cmp={com.android.vending/com.google.android.finsky.services.ContentSyncService}"); + }}; + return myRet; + } + static ArrayList getTestData_4_2() + { + ArrayList myRet = new ArrayList() + {{ + add("Alarm Stats:"); + add(" com.google.android.gsf"); + add(" 8417ms running, 204 wakeups"); + add(" 17 alarms: act=com.google.android.intent.action.GTALK_RECONNECT flg=0x4"); + add(" 187 alarms: flg=0x4"); + add(" com.carl.trafficcounter"); + add(" 446486ms running, 5584 wakeups"); + add(" 5584 alarms: act=com.carl.trafficcounter.UPDATE_RUN flg=0x4"); + }}; + + return myRet; + } + +} diff --git a/androidCommon/src/main/java/com/asksven/android/common/kernelutils/AlarmsDumpsys.java b/androidCommon/src/main/java/com/asksven/android/common/kernelutils/AlarmsDumpsys.java new file mode 100644 index 0000000..6fb0611 --- /dev/null +++ b/androidCommon/src/main/java/com/asksven/android/common/kernelutils/AlarmsDumpsys.java @@ -0,0 +1,448 @@ +/** + * + */ +package com.asksven.android.common.kernelutils; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import android.os.Build; +import android.util.Log; + +//import com.asksven.andoid.common.contrib.Shell; +import com.asksven.andoid.common.contrib.Util; +import com.asksven.android.common.RootShell; +import com.asksven.android.common.privateapiproxies.Alarm; +import com.asksven.android.common.privateapiproxies.StatElement; +import com.asksven.android.common.shellutils.Exec; +import com.asksven.android.common.shellutils.ExecResult; + +/** + * Parses the content of 'dumpsys alarm' + * processes the result of 'dumpsys alarm' as explained in KB article + * https://github.com/asksven/BetterBatteryStats-Knowledge-Base/wiki/AlarmManager + * @author sven + */ +public class AlarmsDumpsys +{ + static final String TAG = "AlarmsDumpsys"; + static final String PERMISSION_DENIED = "su rights required to access alarms are not available / were not granted"; + + public static ArrayList getAlarms() + { + String release = Build.VERSION.RELEASE; + int sdk = Build.VERSION.SDK_INT; + Log.i(TAG, "getAlarms: SDK=" + sdk + ", RELEASE=" + release); + + List res = RootShell.getInstance().run("dumpsys alarm"); + + if (sdk < 17) // Build.VERSION_CODES.JELLY_BEAN_MR1) + { + return getAlarmsPriorTo_4_2_2(res); + } + else if (sdk == Build.VERSION_CODES.JELLY_BEAN_MR1) + { + if (release.equals("4.2.2")) + { + return getAlarmsFrom_4_2_2(res); + } + else + { + return getAlarmsPriorTo_4_2_2(res); + } + } + + else + { + return getAlarmsFrom_4_3(res); + } + } + /** + * Returns a list of alarm value objects + * @return + * @throws Exception + */ + protected static ArrayList getAlarmsPriorTo_4_2_2(List res) + { + ArrayList myAlarms = null; + long nTotalCount = 0; + +// if (res.getSuccess()) + if ((res != null) && (res.size() != 0)) + + { +// String strRes = res.getResultLine(); + if (true) //strRes.contains("Permission Denial")) + { + Pattern begin = Pattern.compile("Alarm Stats"); + boolean bParsing = false; +// ArrayList myRes = res.getResult(); // getTestData(); + + // we are looking for multiline entries in the format + // ' + // '