You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
xiaomibianqian/src/main/java/net/micode/notes/ui/NewAlarmAlertActivity.java

465 lines
18 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* 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 net.micode.notes.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Vibrator;
import android.util.Log;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.tool.DataUtils;
public class NewAlarmAlertActivity extends Activity {
private static final String TAG = "NewAlarmAlertActivity";
private long mNoteId;
private String mSnippet;
private MediaPlayer mPlayer;
private static final int SNIPPET_PREW_MAX_LEN = 60;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate called");
// 设置一个简单的透明布局确保Activity能够正常创建
setContentView(R.layout.alarm_alert);
// 直接初始化并显示对话框
// 检查启动来源,避免在不应该显示的时候显示
if (!isValidLaunch()) {
Log.d(TAG, "Invalid launch, finishing activity");
finish();
return;
}
// 设置窗口属性,确保在任何情况下都能显示
setupWindow();
// 处理Intent数据
processIntent();
// 显示提醒对话框
showAlertDialog();
// 播放提醒声音
playAlarmSound();
// 触发震动提醒
vibrate();
}
/**
* 检查启动来源是否有效
*/
private boolean isValidLaunch() {
Intent intent = getIntent();
if (intent == null) {
Log.e(TAG, "Intent is null");
return false;
}
// 检查是否是系统广播,如果是则不启动提醒窗口
String action = intent.getAction();
if (action != null) {
Log.d(TAG, "Received action: " + action);
// 跳过所有系统广播触发的提醒
if (action.equals(Intent.ACTION_BOOT_COMPLETED) ||
action.equals(Intent.ACTION_TIME_CHANGED) ||
action.equals(Intent.ACTION_TIMEZONE_CHANGED) ||
action.equals(Intent.ACTION_REBOOT) ||
action.equals(Intent.ACTION_SHUTDOWN) ||
action.equals(Intent.ACTION_USER_PRESENT) ||
action.equals(Intent.ACTION_SCREEN_ON) ||
action.equals(Intent.ACTION_SCREEN_OFF)) {
Log.d(TAG, "Skipping alert for system action: " + action);
return false;
}
}
// 简化检查:只要不是系统广播,就认为是有效的启动
// 这样可以确保任何来自AlarmManager或AlarmReceiver的启动都能通过
Log.d(TAG, "Valid launch: non-system broadcast");
return true;
}
private void setupWindow() {
// 无标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);
// 获取窗口
final Window win = getWindow();
// 设置窗口标志,确保在锁屏状态下也能显示
win.addFlags(
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
);
// 设置窗口属性
WindowManager.LayoutParams params = win.getAttributes();
params.width = WindowManager.LayoutParams.MATCH_PARENT;
params.height = WindowManager.LayoutParams.MATCH_PARENT;
params.alpha = 1.0f;
params.dimAmount = 0.7f;
win.setAttributes(params);
}
private void processIntent() {
Intent intent = getIntent();
Log.d(TAG, "Processing intent: " + intent);
// 尝试获取noteId
if (intent != null) {
// 从data中获取
if (intent.getData() != null) {
try {
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
Log.d(TAG, "Note ID from data: " + mNoteId);
} catch (Exception e) {
Log.e(TAG, "Error parsing note ID from data", e);
}
}
// 从extras中获取
if (mNoteId == 0 && intent.getExtras() != null) {
mNoteId = intent.getLongExtra("note_id", 0);
if (mNoteId == 0) {
mNoteId = intent.getLongExtra("noteId", 0);
}
if (mNoteId == 0) {
mNoteId = intent.getLongExtra(Intent.EXTRA_UID, 0);
}
Log.d(TAG, "Note ID from extras: " + mNoteId);
}
// 获取笔记内容
if (mNoteId > 0) {
try {
mSnippet = DataUtils.getSnippetById(getContentResolver(), mNoteId);
if (mSnippet == null || mSnippet.isEmpty()) {
mSnippet = "提醒时间到";
} else if (mSnippet.length() > SNIPPET_PREW_MAX_LEN) {
mSnippet = mSnippet.substring(0, SNIPPET_PREW_MAX_LEN) + "...";
}
} catch (Exception e) {
Log.e(TAG, "Error getting note snippet", e);
mSnippet = "提醒时间到";
}
} else {
mSnippet = "提醒时间到";
}
} else {
mSnippet = "提醒时间到";
}
Log.d(TAG, "Final noteId: " + mNoteId + ", snippet: " + mSnippet);
}
private void showAlertDialog() {
Log.d(TAG, "Showing alert dialog");
// 直接在UI线程显示对话框不使用Handler确保立即显示
try {
// 使用现代主题,确保对话框在手机顶部弹出
AlertDialog.Builder builder = new AlertDialog.Builder(this, android.R.style.Theme_Material_Dialog_Alert);
builder.setTitle(R.string.app_name);
builder.setMessage(mSnippet);
builder.setCancelable(false);
// 设置按钮
builder.setPositiveButton(R.string.notealert_ok, (dialog, which) -> {
Log.d(TAG, "OK button clicked");
// 停止声音和震动
stopAlarm();
dialog.dismiss();
finish();
});
// 查看笔记按钮
builder.setNegativeButton(R.string.notealert_enter, (dialog, which) -> {
Log.d(TAG, "View note button clicked");
// 停止声音和震动
stopAlarm();
if (mNoteId > 0) {
Intent intent = new Intent(NewAlarmAlertActivity.this, NoteEditActivity.class);
intent.putExtra(Intent.EXTRA_UID, mNoteId);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
dialog.dismiss();
finish();
});
// 添加稍后提醒按钮
builder.setNeutralButton("稍后提醒", (dialog, which) -> {
Log.d(TAG, "Snooze button clicked");
// 停止声音和震动
stopAlarm();
// 设置稍后提醒
scheduleSnooze();
dialog.dismiss();
finish();
});
// 创建并显示对话框
AlertDialog dialog = builder.create();
// 设置对话框属性
dialog.setCanceledOnTouchOutside(false);
// 立即显示对话框,不设置复杂的窗口属性,确保在所有设备上都能显示
dialog.show();
Log.d(TAG, "Alert dialog shown immediately");
} catch (Exception e) {
Log.e(TAG, "Error showing alert dialog", e);
// 如果对话框显示失败,尝试使用系统默认对话框
try {
AlertDialog.Builder simpleBuilder = new AlertDialog.Builder(this);
simpleBuilder.setTitle(R.string.app_name);
simpleBuilder.setMessage("提醒时间到");
simpleBuilder.setCancelable(false);
simpleBuilder.setPositiveButton(android.R.string.ok, (dialog, which) -> {
stopAlarm();
dialog.dismiss();
finish();
});
// 添加稍后提醒按钮
simpleBuilder.setNeutralButton("稍后提醒", (dialog, which) -> {
stopAlarm();
scheduleSnooze();
dialog.dismiss();
finish();
});
AlertDialog simpleDialog = simpleBuilder.create();
simpleDialog.show();
Log.d(TAG, "Simple alert dialog shown");
} catch (Exception ex) {
Log.e(TAG, "Error showing simple dialog", ex);
// 即使对话框完全失败也显示Toast提示
try {
Toast.makeText(this, "提醒时间到", Toast.LENGTH_LONG).show();
Log.d(TAG, "Toast shown as final fallback");
} catch (Exception toastEx) {
Log.e(TAG, "Error showing toast", toastEx);
}
// 停止声音和震动
stopAlarm();
// 3秒后自动退出
new Handler(Looper.getMainLooper()).postDelayed(this::finish, 3000);
}
}
}
private void scheduleSnooze() {
Log.d(TAG, "Scheduling snooze for note: " + mNoteId);
// 设置5分钟后再次提醒
long snoozeTime = System.currentTimeMillis() + 5 * 60 * 1000;
try {
// 创建闹钟Intent
Intent intent = new Intent(this, AlarmReceiver.class);
intent.setAction("net.micode.notes.ALARM_TRIGGERED");
if (mNoteId > 0) {
intent.setData(android.content.ContentUris.withAppendedId(net.micode.notes.data.Notes.CONTENT_NOTE_URI, mNoteId));
intent.putExtra(Intent.EXTRA_UID, mNoteId);
}
intent.putExtra("from_alarm_manager", true);
// 创建PendingIntent
int flags = android.app.PendingIntent.FLAG_UPDATE_CURRENT;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
flags |= android.app.PendingIntent.FLAG_IMMUTABLE;
}
android.app.PendingIntent pendingIntent = android.app.PendingIntent.getBroadcast(
this,
(int) mNoteId,
intent,
flags
);
// 设置闹钟
android.app.AlarmManager alarmManager = (android.app.AlarmManager) getSystemService(ALARM_SERVICE);
if (alarmManager != null) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(android.app.AlarmManager.RTC_WAKEUP, snoozeTime, pendingIntent);
} else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(android.app.AlarmManager.RTC_WAKEUP, snoozeTime, pendingIntent);
} else {
alarmManager.set(android.app.AlarmManager.RTC_WAKEUP, snoozeTime, pendingIntent);
}
Log.d(TAG, "Snooze alarm scheduled for: " + snoozeTime);
}
} catch (Exception e) {
Log.e(TAG, "Error scheduling snooze", e);
}
}
private void playAlarmSound() {
Log.d(TAG, "Playing alarm sound");
try {
// 获取默认闹钟铃声
Uri alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
// 如果没有设置闹钟铃声,使用通知铃声
if (alarmUri == null) {
alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
}
// 如果仍然没有,使用默认铃声
if (alarmUri == null) {
alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
}
if (alarmUri != null) {
Log.d(TAG, "Using ringtone: " + alarmUri);
mPlayer = new MediaPlayer();
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
mPlayer.setDataSource(this, alarmUri);
mPlayer.setLooping(true);
mPlayer.prepare();
mPlayer.start();
Log.d(TAG, "Alarm sound started");
} else {
Log.w(TAG, "No ringtone available");
}
} catch (Exception e) {
Log.e(TAG, "Error playing alarm sound", e);
}
}
private void vibrate() {
Log.d(TAG, "Starting vibration");
try {
Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
if (vibrator != null) {
// 检查设备是否支持震动
if (vibrator.hasVibrator()) {
// 定义震动模式:[延迟, 震动时长, 间隔, 震动时长, ...]
long[] pattern = {0, 1000, 500, 1000, 500, 1000};
// 重复模式:-1表示不重复0表示从索引0开始重复
vibrator.vibrate(pattern, -1);
Log.d(TAG, "Vibration started");
} else {
Log.w(TAG, "Device does not support vibration");
}
} else {
Log.e(TAG, "Vibrator service not available");
}
} catch (Exception e) {
Log.e(TAG, "Error starting vibration", e);
}
}
private void stopAlarm() {
Log.d(TAG, "Stopping alarm");
// 停止播放声音
if (mPlayer != null) {
try {
mPlayer.stop();
mPlayer.release();
mPlayer = null;
Log.d(TAG, "Alarm sound stopped");
} catch (Exception e) {
Log.e(TAG, "Error stopping alarm sound", e);
}
}
// 停止震动
try {
Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
if (vibrator != null) {
vibrator.cancel();
Log.d(TAG, "Vibration stopped");
}
} catch (Exception e) {
Log.e(TAG, "Error stopping vibration", e);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy called");
// 停止所有提醒
stopAlarm();
}
// 静态方法,用于启动提醒
public static void start(Context context, long noteId) {
Log.d(TAG, "Starting NewAlarmAlertActivity for note: " + noteId);
Intent intent = new Intent(context, NewAlarmAlertActivity.class);
intent.putExtra("note_id", noteId);
intent.putExtra("from_alarm_manager", true); // 确保添加启动标记
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
context.startActivity(intent);
Log.d(TAG, "NewAlarmAlertActivity started successfully");
} catch (Exception e) {
Log.e(TAG, "Error starting NewAlarmAlertActivity", e);
// 如果直接启动失败,尝试使用服务
try {
Intent serviceIntent = new Intent(context, NewAlarmAlertService.class);
serviceIntent.putExtra("note_id", noteId);
serviceIntent.putExtra("from_alarm_manager", true); // 确保添加启动标记
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
context.startForegroundService(serviceIntent);
} else {
context.startService(serviceIntent);
}
Log.d(TAG, "NewAlarmAlertService started as fallback");
} catch (Exception ex) {
Log.e(TAG, "Error starting NewAlarmAlertService", ex);
// 如果服务也启动失败尝试显示一个简单的Toast提示
try {
android.widget.Toast.makeText(context, "提醒时间到", android.widget.Toast.LENGTH_LONG).show();
Log.d(TAG, "Toast shown as final fallback");
} catch (Exception toastEx) {
Log.e(TAG, "Error showing toast", toastEx);
}
}
}
}
}