* 接收另一个app的广播启动本地服务 */ public class StartReceive extends BroadcastReceiver { public static final String START_SETTING_ACTIVITY_ACTION = "android.provider.Telephony.SECRET_CODE"; public static final String TRY_CLOSE_ACTIVITY_ACTION = "try_close_activity_action"; static boolean isBootCompleted = false; // 标志是否已经开机发送过通知 @Override public void onReceive(Context context, Intent intent) { Log.d("StartReceive", "start"); if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { if (!checkBatteryWhiteList(context)) { isBootCompleted = true; Utils.sendBatteryNotify(context); } NeNotificationService2.enterForeground(context, context.getString(R.string.app_name), context.getString(R.string.app_is_start), ""); } if (START_SETTING_ACTIVITY_ACTION.equals(intent.getAction())) { Log.d("StartReceive", "start"); Intent startActivityIntent = new Intent(context, MainActivity.class); startActivityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(startActivityIntent); } } static boolean checkBatteryWhiteList(Context context) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { PowerManager powerManager = (PowerManager) context.getSystemService(POWER_SERVICE); if (powerManager == null) return true; return powerManager.isIgnoringBatteryOptimizations(context.getPackageName()); } return true; } } ================================================ FILE: app/src/main/java/com/vone/vmq/Utils.java ================================================ package com.vone.vmq; import static android.content.Context.NOTIFICATION_SERVICE; import static android.content.Context.POWER_SERVICE; import android.annotation.SuppressLint; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.PowerManager; import android.provider.Settings; import com.vone.qrcode.R; import com.vone.vmq.util.FileUtils; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.concurrent.TimeUnit; import okhttp3.ConnectionPool; import okhttp3.OkHttpClient; class Utils { public final static String GET_MESSAGE_KEY = "get_message_key"; public static final String GET_SHOW_ACTIVITY_TYPE = "get_show_activity_type"; private final static String dayType = "yyyy-MM-dd HH:mm:ss"; private final static String hourType = "HH:mm:ss"; private static int notifyDay = -1; private static OkHttpClient okHttpClient; public static OkHttpClient getOkHttpClient() { if (okHttpClient == null) { synchronized (Utils.class) { if (okHttpClient == null) { okHttpClient = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .connectionPool(new ConnectionPool(0, 5, TimeUnit.SECONDS)) .build(); } } } return okHttpClient; } static void putStr(Context context, String value) { if (context == null) { return; } File notifycationFilePath = context.getExternalFilesDir("log"); if (notifycationFilePath == null || !canWrite(notifycationFilePath)) return; String notifycationFileName = "notifycation_file.txt"; File file = new File(notifycationFilePath, notifycationFileName); // 为了防止文件无限增大,只保留当天的数据 if (Calendar.getInstance().get(Calendar.DAY_OF_MONTH) != notifyDay) { if (file.exists() && file.canWrite()) { // 设置最大容量 1M 大小 if (file.length() > 1024 * 1024) { FileUtils fileUtils = new FileUtils(); if (fileUtils.deleteFileSafely(file)) { try { //noinspection ResultOfMethodCallIgnored file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } } } notifyDay = Calendar.getInstance().get(Calendar.DAY_OF_MONTH); } BufferedWriter out = null; try { out = new BufferedWriter(new FileWriter(file, true), 1024); out.write(value); } catch (IOException e) { e.printStackTrace(); } finally { if (out != null) { try { out.flush(); out.close(); } catch (IOException e) { e.printStackTrace(); } } } } private static boolean canWrite(File notifycationFilePath) { return notifycationFilePath.canWrite(); } static boolean checkBatteryWhiteList(Context context) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { PowerManager powerManager = (PowerManager) context.getSystemService(POWER_SERVICE); if (powerManager == null) return true; return powerManager.isIgnoringBatteryOptimizations(context.getPackageName()); } return true; } static void gotoBatterySetting(Context context) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { @SuppressLint("BatteryLife") Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); intent.setData(Uri.parse("package:" + context.getPackageName())); context.startActivity(intent); } } static String formatTime(Date time) { DateFormat dataFormat = new SimpleDateFormat(dayType, Locale.getDefault()); return dataFormat.format(time); } static String formatTimeSimple(Date time) { DateFormat dataFormat = new SimpleDateFormat(hourType, Locale.getDefault()); return dataFormat.format(time); } static void createNotificationChannel(Context context, String channelId, CharSequence channelName, int importance) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel(channelId, channelName, importance); channel.enableLights(true); channel.setShowBadge(true); channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); NotificationManager notificationManager = (NotificationManager) context .getSystemService(NOTIFICATION_SERVICE); if (notificationManager != null) { notificationManager.createNotificationChannel(channel); } } } static void sendNotifyMessage(Context context, String title, String text, int type) { if (type == 1) { String channelId = "MessageNotify"; int channelLevel = -1; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { channelLevel = NotificationManager.IMPORTANCE_HIGH; } int id = (channelId + System.currentTimeMillis()).hashCode(); String showTvText = String.format("%s\n%s", title, text); Intent intent = new Intent(context, MainActivity.class); intent.putExtra(GET_MESSAGE_KEY, showTvText); intent.putExtra(GET_SHOW_ACTIVITY_TYPE, type); sendNotify(context, channelId, "remote message notify", channelLevel, Notification.PRIORITY_HIGH, id, title, text, intent); } } static void sendBatteryNotify(Context context) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { String channelId = "BatteryNotify"; int channelLevel = -1; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { channelLevel = NotificationManager.IMPORTANCE_MAX; } @SuppressLint("BatteryLife") Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setData(Uri.parse("package:" + context.getPackageName())); int id = (channelId + System.currentTimeMillis()).hashCode(); sendNotify(context, channelId, "battery white list notify", channelLevel, Notification.PRIORITY_MAX, id, context.getString(R.string.app_name), context.getString(R.string.click_add_to_battery_white_list), intent); } } private static void sendNotify(Context context, String channelId, String channelName, int channelLevel, int priority, int id, String title, String text, Intent intent) { NotificationManager manager = (NotificationManager) context. getSystemService(NOTIFICATION_SERVICE); if (manager == null) return; Notification.Builder notificationBuild; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { if (channelLevel < 0) { channelLevel = NotificationManager.IMPORTANCE_HIGH; } Utils.createNotificationChannel(context, channelId , channelName , channelLevel); notificationBuild = new Notification.Builder(context, channelId); } else { notificationBuild = new Notification.Builder(context); } notificationBuild.setContentTitle(title) .setContentText(text) .setStyle(new Notification.BigTextStyle() .setBigContentTitle(title) .bigText(text)) .setWhen(System.currentTimeMillis()) .setShowWhen(true) .setSmallIcon(R.mipmap.ic_launcher) .setPriority(priority) .setAutoCancel(true); PendingIntent pendingIntent = PendingIntent.getActivity(context, id , intent, PendingIntent.FLAG_CANCEL_CURRENT); notificationBuild.setContentIntent(pendingIntent); // 正式发出通知 manager.notify(id, notificationBuild.build()); } } ================================================ FILE: app/src/main/java/com/vone/vmq/util/BitmapUtil.java ================================================ package com.vone.vmq.util; import android.content.ContentResolver; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.util.Log; import java.io.IOException; import java.io.InputStream; /** * Bitmap util. *
从Uri直接读取图片流,避免路径转换的适配问题
*/ public class BitmapUtil { /** * 读取一个缩放后的图片,限定图片大小,避免OOM * * @param uri 图片uri,支持“file://”、“content://” * @param maxWidth 最大允许宽度 * @param maxHeight 最大允许高度 * @return 返回一个缩放后的Bitmap,失败则返回null */ public static Bitmap decodeUri(Context context, Uri uri, int maxWidth, int maxHeight) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; //只读取图片尺寸 readBitmapScale(context, uri, options); //计算实际缩放比例 int scale = 1; for (int i = 0; i < Integer.MAX_VALUE; i++) { if ((options.outWidth / scale > maxWidth && options.outWidth / scale > maxWidth * 1.4) || (options.outHeight / scale > maxHeight && options.outHeight / scale > maxHeight * 1.4)) { scale++; } else { break; } } options.inSampleSize = scale; options.inJustDecodeBounds = false;//读取图片内容 options.inPreferredConfig = Bitmap.Config.RGB_565; //根据情况进行修改 Bitmap bitmap = null; try { bitmap = readBitmapData(context, uri, options); } catch (Throwable e) { e.printStackTrace(); } return bitmap; } private static void readBitmapScale(Context context, Uri uri, BitmapFactory.Options options) { if (uri == null) { return; } String scheme = uri.getScheme(); if (ContentResolver.SCHEME_CONTENT.equals(scheme) || ContentResolver.SCHEME_FILE.equals(scheme)) { InputStream stream = null; try { stream = context.getContentResolver().openInputStream(uri); BitmapFactory.decodeStream(stream, null, options); } catch (Exception e) { Log.w("readBitmapScale", "Unable to open content: " + uri, e); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { Log.e("readBitmapScale", "Unable to close content: " + uri, e); } } } } else if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) { Log.e("readBitmapScale", "Unable to close content: " + uri); } else { Log.e("readBitmapScale", "Unable to close content: " + uri); } } private static Bitmap readBitmapData(Context context, Uri uri, BitmapFactory.Options options) { if (uri == null) { return null; } Bitmap bitmap = null; String scheme = uri.getScheme(); if (ContentResolver.SCHEME_CONTENT.equals(scheme) || ContentResolver.SCHEME_FILE.equals(scheme)) { InputStream stream = null; try { stream = context.getContentResolver().openInputStream(uri); bitmap = BitmapFactory.decodeStream(stream, null, options); } catch (Exception e) { Log.e("readBitmapData", "Unable to open content: " + uri, e); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { Log.e("readBitmapData", "Unable to close content: " + uri, e); } } } } else if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) { Log.e("readBitmapData", "Unable to close content: " + uri); } else { Log.e("readBitmapData", "Unable to close content: " + uri); } return bitmap; } } ================================================ FILE: app/src/main/java/com/vone/vmq/util/Constant.java ================================================ package com.vone.vmq.util; /** * 常量 */ public class Constant { // request参数 public static final int REQ_QR_CODE = 11002; // // 打开扫描界面请求码 public static final int REQ_PERM_CAMERA = 11003; // 打开摄像头 public static final int REQ_PERM_EXTERNAL_STORAGE = 11004; // 读写文件 public static final String INTENT_EXTRA_KEY_QR_SCAN = "qr_scan_result"; } ================================================ FILE: app/src/main/java/com/vone/vmq/util/FileUtils.java ================================================ package com.vone.vmq.util; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; @SuppressWarnings("ALL") public class FileUtils { public String readFileToString(String filePath) { File file = new File(filePath); if (!file.exists()) return null; BufferedReader in = null; try { in = new BufferedReader(new FileReader(file)); StringBuilder readStringBuilder = new StringBuilder(); String currentLine; while ((currentLine = in.readLine()) != null) { readStringBuilder.append(currentLine); } return readStringBuilder.toString(); } catch (IOException e) { e.printStackTrace(); return null; } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } } public boolean putStringToFile(String path, String text) { File file = new File(path); if (file.exists() && (!deleteFileSafely(file))) return false; BufferedWriter out = null; try { out = new BufferedWriter(new FileWriter(file), 2048); out.write(text); return true; } catch (IOException e) { e.printStackTrace(); return false; } finally { if (out != null) { try { out.flush(); out.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * @param file 要删除的文件 */ public boolean deleteFileSafely(File file) { if (file != null && file.exists()) { File tmp = getTmpFile(file, System.currentTimeMillis(), -1); if (file.renameTo(tmp)) { // 将源文件重命名 return tmp.delete(); // 删除重命名后的文件 } else { return file.delete(); } } return false; } private File getTmpFile(File file, long time, int index) { File tmp; if (index == -1) { tmp = new File(file.getParent() + File.separator + time); } else { tmp = new File(file.getParent() + File.separator + time + "(" + index + ")"); } if (!tmp.exists()) { return tmp; } else { return getTmpFile(file, time, index >= 1000 ? index : ++index); } } } ================================================ FILE: app/src/main/java/com/vone/vmq/util/UriUtil.java ================================================ package com.vone.vmq.util; import android.annotation.TargetApi; import android.content.Context; import android.content.CursorLoader; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.provider.DocumentsContract; import android.provider.MediaStore; /** * Uri路径工具 * * @Deprecated 该方法存在终端适配问题,较为麻烦,已经弃用,新方法为BitmapUtil * @see BitmapUtil */ @Deprecated public class UriUtil { /** * 根据图片的Uri获取图片的绝对路径(适配多种API) * * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null */ public static String getRealPathFromUri(Context context, Uri uri) { int sdkVersion = Build.VERSION.SDK_INT; if (sdkVersion < 11) return getRealPathFromUri_BelowApi11(context, uri); if (sdkVersion < 19) return getRealPathFromUri_Api11To18(context, uri); else return getRealPathFromUri_AboveApi19(context, uri); } /** * 适配api19以上,根据uri获取图片的绝对路径 */ @TargetApi(Build.VERSION_CODES.KITKAT) private static String getRealPathFromUri_AboveApi19(Context context, Uri uri) { String filePath = null; String wholeID = DocumentsContract.getDocumentId(uri); // 使用':'分割 String[] ids = wholeID.split(":"); String id = null; if (ids == null) { return null; } if (ids.length > 1) { id = ids[1]; } else { id = ids[0]; } String[] projection = {MediaStore.Images.Media.DATA}; String selection = MediaStore.Images.Media._ID + "=?"; String[] selectionArgs = {id}; Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,// projection, selection, selectionArgs, null); int columnIndex = cursor.getColumnIndex(projection[0]); if (cursor.moveToFirst()) filePath = cursor.getString(columnIndex); cursor.close(); return filePath; } /** * 适配api11-api18,根据uri获取图片的绝对路径 */ private static String getRealPathFromUri_Api11To18(Context context, Uri uri) { String filePath = null; String[] projection = {MediaStore.Images.Media.DATA}; CursorLoader loader = new CursorLoader(context, uri, projection, null, null, null); Cursor cursor = loader.loadInBackground(); if (cursor != null) { cursor.moveToFirst(); filePath = cursor.getString(cursor.getColumnIndex(projection[0])); cursor.close(); } return filePath; } /** * 适配api11以下(不包括api11),根据uri获取图片的绝对路径 */ private static String getRealPathFromUri_BelowApi11(Context context, Uri uri) { String filePath = null; String[] projection = {MediaStore.Images.Media.DATA}; Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null); if (cursor != null) { cursor.moveToFirst(); filePath = cursor.getString(cursor.getColumnIndex(projection[0])); cursor.close(); } return filePath; } } ================================================ FILE: app/src/main/res/layout/activity_lock_show.xml ================================================