/* DConnectUtil.java Copyright (c) 2014 NTT DOCOMO,INC. Released under the MIT license http://opensource.org/licenses/mit-license.php */ package org.deviceconnect.android.manager.util; import android.Manifest; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.drawable.Drawable; import android.net.Uri; import android.net.wifi.WifiManager; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.PowerManager; import android.provider.Settings; import org.deviceconnect.android.activity.PermissionUtility; import org.deviceconnect.android.manager.DConnectSettings; import org.deviceconnect.message.DConnectMessage; import org.deviceconnect.message.intent.message.IntentDConnectMessage; import org.deviceconnect.utils.JSONUtils; import org.json.JSONException; import org.json.JSONObject; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Iterator; import java.util.Random; import java.util.UUID; /** * ユーティリティクラス. * @author NTT DOCOMO, INC. */ public final class DConnectUtil { /** 乱数の最大値. */ private static final int MAX_NUM = 10000; /** キーワードの桁数を定義. */ private static final int DIGIT = 4; /** 10進数の定義. */ private static final int DECIMAL = 10; /** * Defined the permission. */ public static final String[] PERMISSIONS = new String[] { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; /** * コンストラクタ. * ユーティリティクラスなので、privateとしておく。 */ private DConnectUtil() { } /** * キーワードを作成する. * * @return キーワード */ public static String createKeyword() { StringBuilder builder = new StringBuilder(); builder.append("DCONNECT-"); int rand = Math.abs(new Random().nextInt() % MAX_NUM); for (int i = 0; i < DIGIT; i++) { int r = rand % DECIMAL; builder.append(r); rand /= DECIMAL; } return builder.toString(); } /** * Device Connect Managerの名前を生成する. * * @return Device Connect Managerの名前 */ public static String createName() { StringBuilder builder = new StringBuilder(); builder.append("Manager-"); int rand = Math.abs(new Random().nextInt() % MAX_NUM); for (int i = 0; i < DIGIT; i++) { int r = rand % DECIMAL; builder.append(r); rand /= DECIMAL; } return builder.toString(); } /** * Device Connect Managerの識別子を生成する. * * @return Device Connect Managerの識別子 */ public static String createUuid() { return UUID.randomUUID().toString(); } /** * HttpメソッドをDConnectメソッドに変換する. * @param method 変換するHttpメソッド * @return DConnectメソッド */ public static String convertHttpMethod2DConnectMethod(final String method) { if (DConnectMessage.METHOD_GET.equalsIgnoreCase(method)) { return IntentDConnectMessage.ACTION_GET; } else if (DConnectMessage.METHOD_POST.equalsIgnoreCase(method)) { return IntentDConnectMessage.ACTION_POST; } else if (DConnectMessage.METHOD_PUT.equalsIgnoreCase(method)) { return IntentDConnectMessage.ACTION_PUT; } else if (DConnectMessage.METHOD_DELETE.equalsIgnoreCase(method)) { return IntentDConnectMessage.ACTION_DELETE; } return null; } /** * ファイルへのURIを作成する. * @param uri ファイルへのContentUri * @return URI */ private static String createUri(final String uri) { DConnectSettings settings = DConnectSettings.getInstance(); StringBuilder builder = new StringBuilder(); builder.append(settings.isSSL() ? "https://" : "http://"); builder.append(settings.getHost()); builder.append(":"); builder.append(settings.getPort()); builder.append("/gotapi/files"); builder.append("?uri="); try { builder.append(URLEncoder.encode(uri, "UTF-8")); } catch (UnsupportedEncodingException e) { throw new RuntimeException("Failed to convert a uri."); } return builder.toString(); } /** * JSONの中に入っているuriを変換する. * * <p> * 変換するuriはcontent://から始まるuriのみ変換する。<br/> * それ以外のuriは何も処理しない。 * </p> * * @param root 変換するJSONObject * @throws JSONException JSONの解析に失敗した場合 */ private static void convertUri(final JSONObject root) throws JSONException { @SuppressWarnings("unchecked") // Using legacy API Iterator<String> it = root.keys(); while (it.hasNext()) { String key = it.next(); Object value = root.opt(key); if (value instanceof String) { if ("uri".equals(key) && startWithContent((String) value)) { String u = createUri((String) value); root.put(key, u); } } else if (value instanceof JSONObject) { convertUri((JSONObject) value); } } } /** * 指定されたuriがcontent://から始まるかチェックする. * @param uri チェックするuri * @return content://から始まる場合はtrue、それ以外はfalse */ private static boolean startWithContent(final String uri) { return uri != null && (uri.startsWith("content://")); } /** * BundleからJSONObjectに変換する. * @param root JSONObjectに変換したデータを格納するオブジェクト * @param b 変換するBundle * @throws JSONException JSONへの変換に失敗した場合に発生 */ public static void convertBundleToJSON( final JSONObject root, final Bundle b) throws JSONException { JSONUtils.convertBundleToJSON(root, b); convertUri(root); } /** * AndroidManifest.xmlのversionNameを取得する. * * @param context Context * @return versionName */ public static String getVersionName(final Context context) { PackageManager packageManager = context.getPackageManager(); try { PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES); return packageInfo.versionName; } catch (NameNotFoundException e) { return "Unknown"; } } /** * Gets the ip address. * @param context Context of application * @return Returns ip address */ public static String getIPAddress(final Context context) { WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); int ipAddress = wifiManager.getConnectionInfo().getIpAddress(); return String.format("%d.%d.%d.%d", (ipAddress & 0xff), (ipAddress >> 8 & 0xff), (ipAddress >> 16 & 0xff), (ipAddress >> 24 & 0xff)); } /** * Checks whether permission allow by user. * @param context context of application * @return Returns true if permission allow, otherwise false */ public static boolean isPermission(final Context context) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { return true; } else { boolean result = true; for (int i = 0; i < PERMISSIONS.length; i++) { if (context.checkSelfPermission(PERMISSIONS[i]) != PackageManager.PERMISSION_GRANTED) { result = false; } } return result; } } /** * ストレージのパーミッションを要求します. * @param context コンテキスト * @param callback パーミッションの許諾を通知するコールバック */ public static void requestPermission(final Context context, final Handler handler, final PermissionUtility.PermissionRequestCallback callback) { PermissionUtility.requestPermissions(context, handler, PERMISSIONS, callback); } /** * 指定されたDrawableをグレースケール変換をする. * @param drawable 変換するDrawable * @return 変換後のDrawable */ public static Drawable convertToGrayScale(final Drawable drawable) { Drawable clone = drawable.getConstantState().newDrawable().mutate(); ColorMatrix matrix = new ColorMatrix(); matrix.setSaturation(0.2f); ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix); clone.setColorFilter(filter); return clone; } /** マスクを定義. */ private static final int MASK = 0xFF; /** * バイト配列を16進数の文字列に変換する. * @param buf 文字列に変換するバイト * @return 文字列 */ private static String hexToString(final byte[] buf) { StringBuilder hexString = new StringBuilder(); for (int i = 0; i < buf.length; i++) { hexString.append(Integer.toHexString(MASK & buf[i])); } return hexString.toString(); } /** * 指定された文字列をMD5の文字列に変換する. * <p> * MD5への変換に失敗した場合には{@code null}を返却する。 * </p> * @param s MD5にする文字列 * @return MD5にされた文字列 * @throws UnsupportedEncodingException 文字列の解析に失敗した場合 * @throws NoSuchAlgorithmException MD5がサポートされていない場合 */ public static String toMD5(final String s) throws UnsupportedEncodingException, NoSuchAlgorithmException { MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); digest.update(s.getBytes("ASCII")); return hexToString(digest.digest()); } /** * 指定されたContextのアプリケーションがDozeモードが有効になっているかを確認する. * <p> * Android M以前のOSでは、常にtrueを返却します。 * </p> * @param context コンテキスト * @return Dozeモードが有効の場合はtrue、それ以外はfalse */ public static boolean isDozeMode(final Context context) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { return true; } else { PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); return powerManager.isIgnoringBatteryOptimizations(context.getPackageName()); } } /** * Dozeモードの解除要求を行う. * <p> * Dozeモードの解除には必ずユーザの許諾が必要になります。 * </p> * <p> * Android M以前のOSの場合には、このメソッドは処理を行いません。 * </p> * @param context コンテキスト */ public static void startConfirmIgnoreDozeMode(final Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); intent.setData(Uri.parse("package:" + context.getPackageName())); context.startActivity(intent); } } /** * Dozeモードの設定画面を開きます. * <p> * Dozeモードの解除には必ずユーザの許諾が必要になります。 * </p> * <p> * Android M以前のOSの場合には、このメソッドは処理を行いません。 * </p> * @param context コンテキスト */ public static void startDozeModeSettingActivity(final Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Intent intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } } }