Java tutorial
/* * Copyright (C) 2014 OTA Update Center * * 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.otaupdater.utils; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageInfo; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.wifi.WifiManager; import android.os.AsyncTask; import android.os.Build; import android.provider.Settings; import android.telephony.TelephonyManager; import android.util.Log; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesUtil; import com.google.android.gms.gcm.GoogleCloudMessaging; import com.otaupdater.R; import com.otaupdater.SettingsActivity; import org.json.JSONObject; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.security.MessageDigest; import java.security.SecureRandom; import java.text.Normalizer; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.Random; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; public class Utils { // protected static final int REQUEST_CODE_RECOVER_PLAY_SERVICES = 1001; private static final Random random = new SecureRandom(); private static final SimpleDateFormat OTA_DATE = new SimpleDateFormat("yyyyMMdd-kkmm", Locale.US); private static Boolean cachedPlayServicesCheck = null; private Utils() { } public static int getAppVersion(Context ctx) { try { PackageInfo packageInfo = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), 0); return packageInfo.versionCode; } catch (Exception e) { // should never happen throw new RuntimeException("Could not get package name: " + e); } } public static String md5(String s) { try { MessageDigest digest = MessageDigest.getInstance("MD5"); digest.update(s.getBytes()); return byteArrToStr(digest.digest()); } catch (Exception e) { e.printStackTrace(); } return ""; } private static final HashMap<File, String> MD5_FILE_CACHE = new HashMap<File, String>(); public static String md5(File f) { if (!f.exists()) return ""; if (MD5_FILE_CACHE.containsKey(f)) { String cachedMD5 = MD5_FILE_CACHE.get(f); int cachedMD5Split = cachedMD5.indexOf(':'); long lastModified = Long.parseLong(cachedMD5.substring(cachedMD5Split + 1)); if (lastModified == f.lastModified()) { return cachedMD5.substring(0, cachedMD5Split); } else { MD5_FILE_CACHE.remove(f); } } InputStream in = null; try { in = new FileInputStream(f); MessageDigest digest = MessageDigest.getInstance("MD5"); byte[] buf = new byte[4096]; int nRead; while ((nRead = in.read(buf)) != -1) { digest.update(buf, 0, nRead); } String md5 = byteArrToStr(digest.digest()); MD5_FILE_CACHE.put(f, md5 + ":" + Long.toString(f.lastModified())); return md5; } catch (Exception e) { e.printStackTrace(); } finally { if (in != null) { try { in.close(); } catch (IOException ignored) { } } } return ""; } public static String hmac(String str, String key) { try { Mac mac = Mac.getInstance(Config.HMAC_ALGORITHM); String salt = randomSaltString(mac.getMacLength()); mac.init(new SecretKeySpec(key.getBytes(), mac.getAlgorithm())); return byteArrToStr(mac.doFinal((salt + str + salt).getBytes("UTF-8"))) + salt; } catch (Exception e) { e.printStackTrace(); } return null; } // public static void toastWrapper(final Activity activity, final CharSequence text, final int duration) { // activity.runOnUiThread(new Runnable() { // @Override public void run() { // Toast.makeText(activity, text, duration).show(); // } // }); // } // // public static void toastWrapper(final Activity activity, final int resId, final int duration) { // activity.runOnUiThread(new Runnable() { // @Override public void run() { // Toast.makeText(activity, resId, duration).show(); // } // }); // } // // public static void toastWrapper(final View view, final CharSequence text, final int duration) { // view.post(new Runnable() { // @Override public void run() { // Toast.makeText(view.getContext(), text, duration).show(); // } // }); // } // // public static void toastWrapper(final View view, final int resId, final int duration) { // view.post(new Runnable() { // @Override public void run() { // Toast.makeText(view.getContext(), resId, duration).show(); // } // }); // } public static boolean checkPlayServices(Context ctx) { if (cachedPlayServicesCheck == null) { int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(ctx); if (resultCode == ConnectionResult.SUCCESS) { cachedPlayServicesCheck = true; } else { if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) { Log.v(Config.LOG_TAG + "checkPlayServices", "Play Services error: " + GooglePlayServicesUtil.getErrorString(resultCode)); // if (ctx instanceof Activity) { // GooglePlayServicesUtil.getErrorDialog(resultCode, (Activity) ctx, REQUEST_CODE_RECOVER_PLAY_SERVICES).show(); // } } else { Log.v(Config.LOG_TAG + "checkPlayServices", "Device not supported"); } cachedPlayServicesCheck = false; } } return cachedPlayServicesCheck; } public static boolean dataAvailable(Context ctx) { ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo ni = cm.getActiveNetworkInfo(); return ni != null && ni.isConnected(); } public static boolean wifiConnected(Context ctx) { ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo ni = cm.getActiveNetworkInfo(); return ni != null && ni.isConnected() && ni.getType() == ConnectivityManager.TYPE_WIFI; } public static Date parseDate(String date) { if (date == null) return null; try { return OTA_DATE.parse(date); } catch (ParseException e) { e.printStackTrace(); } return null; } public static String formatDate(Date date) { if (date == null) return null; return OTA_DATE.format(date); } public static void updateDeviceRegistration(final Context ctx) { final Config cfg = Config.getInstance(ctx); final APIUtils.APIAdapter regCallback = new APIUtils.APIAdapter() { @Override public void onSuccess(String message, JSONObject respObj) { cfg.setPingedCurrent(); if (PropUtils.isRomOtaEnabled()) { RomInfo info = RomInfo.FACTORY.fromJSON(respObj.optJSONObject(RomInfo.KEY_NAME)); if (info != null && info.isUpdate()) { cfg.storeRomUpdate(info); if (cfg.getShowNotif()) { info.showUpdateNotif(ctx); } else { Log.v(Config.LOG_TAG + "DeviceRegister", "got rom update response, notif not shown"); } } else { cfg.clearStoredRomUpdate(); RomInfo.FACTORY.clearUpdateNotif(ctx); } } if (PropUtils.isKernelOtaEnabled()) { KernelInfo info = KernelInfo.FACTORY.fromJSON(respObj.optJSONObject(KernelInfo.KEY_NAME)); if (info != null && info.isUpdate()) { cfg.storeKernelUpdate(info); if (cfg.getShowNotif()) { info.showUpdateNotif(ctx); } else { Log.v(Config.LOG_TAG + "DeviceRegister", "got kernel update response, notif not shown"); } } else { cfg.clearStoredKernelUpdate(); KernelInfo.FACTORY.clearUpdateNotif(ctx); } } } @Override public void onError(String message, JSONObject respObj) { cfg.setGcmRegistrationId(null); //TODO maybe do something better? Log.w(Config.LOG_TAG + "DeviceRegister", "error registering with server: " + message); } }; if (checkPlayServices(ctx)) { String regId = cfg.getGcmRegistrationId(); if (regId == null) { Log.v(Config.LOG_TAG + "DeviceRegister", "Not registered, registering for GCM..."); new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { try { GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(ctx); String regID = gcm.register(Config.GCM_SENDER_ID); cfg.setGcmRegistrationId(regID); Log.v(Config.LOG_TAG + "DeviceRegister", "GCM registered"); APIUtils.updateDeviceRegistration(ctx, regCallback); } catch (Exception ex) { Log.e(Config.LOG_TAG + "DeviceRegister", "Error registering GCM: " + ex.getMessage()); } return null; } }.execute(); } else if (!cfg.upToDate()) { Log.v(Config.LOG_TAG + "DeviceRegister", "Already GCM registered, out-of-date"); cfg.setValuesToCurrent(); APIUtils.updateDeviceRegistration(ctx, regCallback); } else if (cfg.needPing()) { Log.v(Config.LOG_TAG + "DeviceRegister", "Already GCM registered, need to ping"); APIUtils.doPing(ctx, new APIUtils.APIAdapter() { @Override public void onSuccess(String message, JSONObject respObj) { cfg.setPingedCurrent(); } }); } else { Log.v(Config.LOG_TAG + "DeviceRegister", "Already GCM registered, no ping necessary"); } } else if (!cfg.upToDate()) { Log.v(Config.LOG_TAG + "DeviceRegister", "Already registered, out-of-date"); cfg.setValuesToCurrent(); APIUtils.updateDeviceRegistration(ctx, regCallback); } else if (cfg.needPing()) { Log.v(Config.LOG_TAG + "DeviceRegister", "Already registered, need to ping"); APIUtils.doPing(ctx, new APIUtils.APIAdapter() { @Override public void onSuccess(String message, JSONObject respObj) { cfg.setPingedCurrent(); } }); } else { Log.v(Config.LOG_TAG + "DeviceRegister", "Already registered, no ping necessary"); } } private static String device = null; public static String getDevice() { if (device != null) return device; device = Build.DEVICE.toLowerCase(Locale.US); return device; } private static String deviceID = null; public static String getDeviceID(Context ctx) { if (deviceID != null) return deviceID; deviceID = ((TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId(); if (deviceID == null) { WifiManager wm = (WifiManager) ctx.getSystemService(Context.WIFI_SERVICE); if (wm.isWifiEnabled()) { deviceID = wm.getConnectionInfo().getMacAddress(); } else { //fallback to ANDROID_ID - gets reset on data wipe, but it's better than nothing deviceID = Settings.Secure.getString(ctx.getContentResolver(), Settings.Secure.ANDROID_ID); } } deviceID = md5(deviceID); return deviceID; } private static String deviceName = null; public static String getDeviceName(Context ctx) { if (deviceName != null) return deviceName; deviceName = ((TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE)).getNetworkOperatorName(); if (deviceName == null || deviceName.isEmpty()) deviceName = "Wi-Fi"; deviceName += " " + Build.MODEL; return deviceName.trim(); } public static String sanitizeName(String name) { if (name == null) return ""; name = Normalizer.normalize(name, Normalizer.Form.NFD); name = name.trim(); name = name.replaceAll("[^\\p{ASCII}]", ""); name = name.replaceAll("[ _-]+", "_"); name = name.replaceAll("(^_|_$)", ""); name = name.toLowerCase(Locale.US); return name; } public static void showProKeyOnlyFeatureDialog(final Context ctx, final DialogCallback callback) { AlertDialog.Builder builder = new AlertDialog.Builder(ctx); builder.setTitle(R.string.prokey_only_feature_title); builder.setMessage(R.string.prokey_only_feature_message); builder.setPositiveButton(R.string.prokey_only_get, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); Intent i = new Intent(ctx, SettingsActivity.class); i.setAction(SettingsActivity.EXTRA_SHOW_GET_PROKEY_DLG); i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); ctx.startActivity(i); } }); builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); final AlertDialog dlg = builder.create(); dlg.setOnShowListener(new DialogInterface.OnShowListener() { @Override public void onShow(DialogInterface dialog) { if (callback != null) callback.onDialogShown(dlg); } }); dlg.setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { if (callback != null) callback.onDialogClosed(dlg); } }); dlg.show(); } private static final char[] HEX_DIGITS = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; public static String byteArrToStr(byte[] bytes) { StringBuilder str = new StringBuilder(); for (byte b : bytes) { str.append(HEX_DIGITS[(0xF0 & b) >>> 4]); str.append(HEX_DIGITS[0xF & b]); } return str.toString(); } public static byte[] randomSalt(int bytes) { byte[] b = new byte[bytes]; random.nextBytes(b); return b; } public static String randomSaltString(int bytes) { return byteArrToStr(randomSalt(bytes)); } }