- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我目前正在开发一个支持环境 mjode 的应用程序。那没什么难的。现在,我的应用程序有两个需要每秒更新一次的计时器。所以我去找谷歌文档here .我已经尝试了很多次,我已经检查了文档建议的每一行代码,但我无法让它正常工作。
当模拟器或 watch 进入环境模式时,计时器会持续几秒钟,然后停止几秒钟。然后更新 1-3 次,然后再次停止。奇怪的是,这种情况没有任何模式发生。
我对如何处理这个问题一无所知。
编辑:在模拟器中运行时,似乎或多或少是 5 秒的间隔。但是,当我将 triggerTimeMs - CurrenttimeMs 输出到日志时,它始终显示 1,这在技术上意味着警报已安排在下一秒。
这是我的代码:
import android.app.AlarmManager;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.*;
import android.support.constraint.ConstraintLayout;
import android.support.v4.app.FragmentActivity;
import android.support.v4.content.ContextCompat;
import android.support.wear.ambient.AmbientModeSupport;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Chronometer;
import android.widget.TextClock;
import android.widget.TextView;
import android.widget.Toast;
import android.support.v4.app.NotificationCompat;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class TimerActivity extends FragmentActivity implements AmbientModeSupport.AmbientCallbackProvider {
@Override
public AmbientModeSupport.AmbientCallback getAmbientCallback() {
return new MyAmbientCallback();
}
private static final String AMBIENT_UPDATE_ACTION = "de.jannisgoeing.sportstimerforwearos.action.AMBIENT_UPDATE";
private static final int BURN_IN_OFFSET_PX = 10;
boolean mIsLowBitAmbient;
boolean mDoBurnInProtection;
private AlarmManager mAmbientUpdateAlarmManager;
private PendingIntent mAmbientUpdatePendingIntent;
private BroadcastReceiver mAmbientUpdateBroadcastReceiver;
private AmbientModeSupport.AmbientController mAmbientController;
private ConstraintLayout timer_layout;
private TextClock clock;
private TextView timerName;
private TextView sectionName;
private long pauseOffset;
private Intent intent;
private Timer timer;
private Button timerButton;
private int timerStatus = 0;
private TimerSection currentTimerSection;
private int prev_timer_status;
private TextView label_current;
private TextView label_actual;
private Chronometer current;
private Chronometer actual;
private Boolean running;
private NotificationManager notificationManager;
@SuppressWarnings( "deprecation" )
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timer);
// Enables Always-on
mAmbientController = AmbientModeSupport.attach(this);
mAmbientUpdateAlarmManager =
(AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent ambientUpdateIntent = new Intent(AMBIENT_UPDATE_ACTION);
mAmbientUpdatePendingIntent = PendingIntent.getBroadcast(
this, 0, ambientUpdateIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mAmbientUpdateBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
refreshDisplayAndSetNextUpdate();
}
};
final Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
notificationManager = getSystemService(NotificationManager.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager.createNotificationChannel(
new NotificationChannel("timer", "Timer", NotificationManager.IMPORTANCE_HIGH)
);
}
intent = this.getIntent();
if (intent.getExtras() == null) {
Toast.makeText(this, R.string.error_noProfilePassed, Toast.LENGTH_LONG).show();
intent = new Intent(this, MainActivity.class);
this.startActivity(intent);
} else {
timer = (Timer) intent.getSerializableExtra("TIMER");
}
if (timer == null) {
Toast.makeText(this, R.string.error_noValidTimerPassed, Toast.LENGTH_LONG).show();
intent = new Intent(this, MainActivity.class);
this.startActivity(intent);
}
timer_layout = findViewById(R.id.timer_layout);
clock = findViewById(R.id.textClock);
clock.setFormat24Hour(clock.getFormat24Hour());
timerName = findViewById(R.id.timerName);
timerName.setText(timer.getName());
current = findViewById(R.id.chronometerCurrent);
actual = findViewById(R.id.chronometerActual);
label_current = findViewById(R.id.labelCurrentTime);
label_actual = findViewById(R.id.labelActualTime);
currentTimerSection = getFirstTimerSection(timer);
sectionName = findViewById(R.id.sectionName);
sectionName.setText(currentTimerSection.getName());
running = false;
Intent stopIntent = new Intent("de.jannisgoeing.sportstimerforwearos.STOP");
PendingIntent stopPendingIntent = PendingIntent.getBroadcast(this, 0, stopIntent, 0);
Intent startIntent = new Intent("de.jannisgoeing.sportstimerforwearos.STARTPAUSE");
PendingIntent startPendingIntent = PendingIntent.getBroadcast(this, 0, startIntent, 0);
final NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setWhen(System.currentTimeMillis() - SystemClock.elapsedRealtime() + current.getBase())
.setUsesChronometer(false)
.setContentText("Waiting for Next Section to start.")
.setSmallIcon(timer.getIcon())
.setAutoCancel(false)
.setOngoing(true)
.setOnlyAlertOnce(true)
.setContentIntent(
PendingIntent.getActivity(this, 10,
new Intent(this, TimerActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP),
0)
)
.addAction(running ? R.drawable.ic_pause_white
: R.drawable.ic_play_white,
running ? this.getString(R.string.button_pause)
: this.getString(R.string.button_resume),
startPendingIntent)
.addAction(R.drawable.ic_stop_white, this.getString(R.string.button_end), stopPendingIntent);
notificationManager.notify(1, builder.build());
timerButton = findViewById(R.id.timerButton);
timerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(timerStatus == 0) { //0=Status: Not started
timerStatus = 1;
timerButton.setText(R.string.button_pause);
checkHeadStart();
builder.setWhen(System.currentTimeMillis() - SystemClock.elapsedRealtime() + current.getBase()).setUsesChronometer(true).setContentText("").build();
notificationManager.notify(1, builder.build());
current.start();
actual.start();
running = true;
} else if (timerStatus == 1) { //1=Status: Running
timerStatus = 2;
timerButton.setText(R.string.button_resume);
pauseOffset = SystemClock.elapsedRealtime() - current.getBase();
builder.setUsesChronometer(false).setContentText("Paused").build();
notificationManager.notify(1, builder.build());
current.stop();
running = false;
if(currentTimerSection.getType() == 2) {
actual.stop();
}
} else if (timerStatus == 2) { //2=Status: Paused
timerStatus = 1;
timerButton.setText(R.string.button_pause);
current.setBase(SystemClock.elapsedRealtime() - pauseOffset);
builder.setWhen(System.currentTimeMillis() - SystemClock.elapsedRealtime() + current.getBase()).setUsesChronometer(true).setContentText("").build();
notificationManager.notify(1, builder.build());
current.start();
running = true;
if(currentTimerSection.getType() == 2) {
actual.setBase(SystemClock.elapsedRealtime() - pauseOffset);
actual.start();
}
} else if (timerStatus == 3) { //3=Status: Finished
vibrator.cancel();
currentTimerSection = getNextTimerSection(timer, currentTimerSection.getId());
current.stop();
actual.stop();
running = false;
actual.setFormat("%s");
actual.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.white));
if (currentTimerSection == null) {
notificationManager.cancel(1);
Toast.makeText(getApplicationContext(), R.string.match_finished, Toast.LENGTH_LONG).show();
intent = new Intent(getApplicationContext(), MainActivity.class);
getApplicationContext().startActivity(intent);
} else {
sectionName.setText(currentTimerSection.getName());
timerButton.setText(R.string.button_start);
timerStatus = 0;
checkHeadStart();
builder.setWhen(System.currentTimeMillis() - SystemClock.elapsedRealtime() + current.getBase()).setUsesChronometer(false).setContentText("Waiting for Next Section to start.").build();
notificationManager.notify(1, builder.build());
}
}
}
});
timerButton.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if(timerStatus != 3) {
prev_timer_status = timerStatus;
timerStatus = 3;
timerButton.setText(R.string.button_end);
return true;
} else {
timerStatus = prev_timer_status;
if(timerStatus == 1) {
timerButton.setText(R.string.button_pause);
} else if (timerStatus == 2) {
timerButton.setText(R.string.button_resume);
} else if (timerStatus == 0) {
timerButton.setText(R.string.button_start);
}
return true;
}
}
});
actual.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
@Override
public void onChronometerTick(Chronometer chronometer) {
if(SystemClock.elapsedRealtime() - chronometer.getBase() >= currentTimerSection.getMinutes()*60000 + currentTimerSection.getSeconds()*1000 + currentTimerSection.getMinutesHeadStart()*60000 + currentTimerSection.getSecondsHeadStart()*1000 && chronometer.getCurrentTextColor() != ContextCompat.getColor(getApplicationContext(), R.color.red)) {
chronometer.setFormat("+%s");
chronometer.setBase(SystemClock.elapsedRealtime());
chronometer.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.red));
long[] timings = {0, 500, 500, 500, 500};
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createWaveform(timings, 0));
} else {
vibrator.vibrate(timings, 0);
}
}
}
});
current.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
@Override
public void onChronometerTick(Chronometer chronometer) {
if(SystemClock.elapsedRealtime() - chronometer.getBase() >= currentTimerSection.getMinutes()*60000 + currentTimerSection.getSeconds()*1000 + currentTimerSection.getMinutesHeadStart()*60000 + currentTimerSection.getSecondsHeadStart()*1000 && timerStatus != 3) {
timerStatus = 3;
timerButton.setText(R.string.button_end);
current.stop();
}
}
});
}
private void checkHeadStart() {
if(currentTimerSection.isTimerHeadStart()) {
current.setBase(SystemClock.elapsedRealtime() - currentTimerSection.getMinutesHeadStart()*60000 - currentTimerSection.getSecondsHeadStart()*1000);
actual.setBase(SystemClock.elapsedRealtime() - currentTimerSection.getMinutesHeadStart()*60000 - currentTimerSection.getSecondsHeadStart()*1000);
} else {
current.setBase(SystemClock.elapsedRealtime());
actual.setBase(SystemClock.elapsedRealtime());
}
}
private static final long AMBIENT_INTERVAL_MS = TimeUnit.SECONDS.toMillis(1);
//private static final long ACTIVE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(1);
//private static final int MSG_UPDATE_SCREEN = 0;
private void refreshDisplayAndSetNextUpdate() {
if (mAmbientController.isAmbient()) {
// Implement data retrieval and update the screen for ambient mode
} else {
// Implement data retrieval and update the screen for interactive mode
}
long timeMs = System.currentTimeMillis();
// Schedule a new alarm
if (mAmbientController.isAmbient()) {
// Calculate the next trigger time
long delayMs = AMBIENT_INTERVAL_MS - (timeMs % AMBIENT_INTERVAL_MS);
long triggerTimeMs = timeMs + delayMs;
mAmbientUpdateAlarmManager.setExact(
AlarmManager.RTC_WAKEUP,
triggerTimeMs,
mAmbientUpdatePendingIntent);
} else {
//long delayMs = ACTIVE_INTERVAL_MS - (timeMs % ACTIVE_INTERVAL_MS);
//mActiveModeUpdateHandler.removeMessages(MSG_UPDATE_SCREEN);
//mActiveModeUpdateHandler.sendEmptyMessageDelayed(MSG_UPDATE_SCREEN, delayMs);
}
}
@Override
public void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter(AMBIENT_UPDATE_ACTION);
registerReceiver(mAmbientUpdateBroadcastReceiver, filter);
refreshDisplayAndSetNextUpdate();
}
@Override
public void onPause() {
super.onPause();
unregisterReceiver(mAmbientUpdateBroadcastReceiver);
mAmbientUpdateAlarmManager.cancel(mAmbientUpdatePendingIntent);
}
@Override
public void onDestroy() {
mAmbientUpdateAlarmManager.cancel(mAmbientUpdatePendingIntent);
notificationManager.cancel(1);
Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
vibrator.cancel();
super.onDestroy();
}
public TimerSection getFirstTimerSection(Timer timer) {
List<TimerSection> timerSectionList = timer.getTimerSections();
for (TimerSection t : timerSectionList) {
if (t.id == 1) {
return t;
}
}
return null;
}
public TimerSection getNextTimerSection(Timer timer, int id) {
List<TimerSection> timerSectionList = timer.getTimerSections();
for(TimerSection t : timerSectionList) {
if (t.id == id + 1) {
return t;
}
}
return null;
}
private class MyAmbientCallback extends AmbientModeSupport.AmbientCallback {
/** Prepares the UI for ambient mode. */
@Override
public void onEnterAmbient(Bundle ambientDetails) {
super.onEnterAmbient(ambientDetails);
timerButton.setVisibility(View.INVISIBLE);
Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
vibrator.cancel();
mIsLowBitAmbient =
ambientDetails.getBoolean(AmbientModeSupport.EXTRA_LOWBIT_AMBIENT, false);
mDoBurnInProtection =
ambientDetails.getBoolean(AmbientModeSupport.EXTRA_BURN_IN_PROTECTION, false);
/* Clears Handler queue (only needed for updates in active mode). */
//mActiveModeUpdateHandler.removeMessages(MSG_UPDATE_SCREEN);
/*
* Following best practices outlined in WatchFaces API (keeping most pixels black,
* avoiding large blocks of white pixels, using only black and white, and disabling
* anti-aliasing, etc.)
*/
if (mIsLowBitAmbient) {
clock.getPaint().setAntiAlias(false);
current.getPaint().setAntiAlias(false);
actual.getPaint().setAntiAlias(false);
timerButton.getPaint().setAntiAlias(false);
label_actual.getPaint().setAntiAlias(false);
label_current.getPaint().setAntiAlias(false);
timerName.getPaint().setAntiAlias(false);
sectionName.getPaint().setAntiAlias(false);
}
refreshDisplayAndSetNextUpdate();
}
@Override
public void onUpdateAmbient() {
super.onUpdateAmbient();
if (mDoBurnInProtection) {
int x = (int) (Math.random() * 2 * BURN_IN_OFFSET_PX - BURN_IN_OFFSET_PX);
int y = (int) (Math.random() * 2 * BURN_IN_OFFSET_PX - BURN_IN_OFFSET_PX);
timer_layout.setPadding(x, y, 0, 0);
}
}
/** Restores the UI to active (non-ambient) mode. */
@Override
public void onExitAmbient() {
super.onExitAmbient();
timerButton.setVisibility(View.VISIBLE);
Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
vibrator.cancel();
/* Clears out Alarms since they are only used in ambient mode. */
mAmbientUpdateAlarmManager.cancel(mAmbientUpdatePendingIntent);
if (mIsLowBitAmbient) {
clock.getPaint().setAntiAlias(true);
current.getPaint().setAntiAlias(true);
actual.getPaint().setAntiAlias(true);
timerButton.getPaint().setAntiAlias(true);
label_actual.getPaint().setAntiAlias(true);
label_current.getPaint().setAntiAlias(true);
timerName.getPaint().setAntiAlias(true);
sectionName.getPaint().setAntiAlias(true);
}
/* Reset any random offset applied for burn-in protection. */
if (mDoBurnInProtection) {
timer_layout.setPadding(0, 0, 0, 0);
}
refreshDisplayAndSetNextUpdate();
}
}
}
最佳答案
首先您可以尝试使用 setExactAndAllowWhileIdle 而不是 setExact 方法来运行警报。最终你可以尝试使用 setAlarmClock 但我认为最可靠将为 Activity 环境模式使用唤醒锁和处理程序。
关于java - 在环境模式下更频繁地更新不起作用 - Android Wear/WearOS,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53533814/
对此感到疯狂,真的缺少一些东西。 我有webpack 4.6.0,webpack-cli ^ 2.1.2,所以是最新的。 在文档(https://webpack.js.org/concepts/mod
object Host "os.google.com" { import "windows" address = "linux.google.com" groups = ["linux"] } obj
每当我安装我的应用程序时,我都可以将数据库从 Assets 文件夹复制到 /data/data/packagename/databases/ .到此为止,应用程序工作得很好。 但 10 或 15 秒后
我在 cc 模式缓冲区中使用 hideshow.el 来折叠我不查看的文件部分。 如果能够在 XML 文档中做到这一点就好了。我使用 emacs 22.2.1 和内置的 sgml-mode 进行 xm
已结束。此问题不符合 Stack Overflow guidelines .它目前不接受答案。 我们不允许提出有关书籍、工具、软件库等方面的建议的问题。您可以编辑问题,以便用事实和引用来回答它。 关闭
根据java: public Scanner useDelimiter(String pattern) Sets this scanner's delimiting pattern to a patt
我读过一些关于 PRG 模式以及它如何防止用户重新提交表单的文章。比如this post有一张不错的图: 我能理解为什么在收到 2xx 后用户刷新页面时不会发生表单提交。但我仍然想知道: (1) 如果
看看下面的图片,您可能会清楚地看到这一点。 那么如何在带有其他一些 View 的简单屏幕中实现没有任何弹出/对话框/模式的微调器日期选择器? 我在整个网络上进行了谷歌搜索,但没有找到与之相关的任何合适
我不知道该怎么做,我一直遇到问题。 以下是代码: rows = int(input()) for i in range(1,rows): for j in range(1,i+1):
我想为重写创建一个正则表达式。 将所有请求重写为 index.php(不需要匹配),它不是以/api 开头,或者不是以('.html',或'.js'或'.css'或'.png'结束) 我的例子还是这样
MVC模式代表 Model-View-Controller(模型-视图-控制器) 模式 MVC模式用于应用程序的分层开发 Model(模型) - 模型代表一个存取数据的对象或 JAVA PO
我想为组织模式创建一个 RDF 模式世界。您可能知道,组织模式文档基于层次结构大纲,其中标题是主要的分组实体。 * March auxiliary :PROPERTIES: :HLEVEL: 1 :E
我正在编写一个可以从文件中读取 JSON 数据的软件。该文件包含“person”——一个值为对象数组的对象。我打算使用 JSON 模式验证库来验证内容,而不是自己编写代码。符合代表以下数据的 JSON
假设我有 4 张 table 人 公司 团体 和 账单 现在bills/persons和bills/companys和bills/groups之间是多对多的关系。 我看到了 4 种可能的 sql 模式
假设您有这样的文档: doc1: id:1 text: ... references: Journal1, 2013, pag 123 references: Journal2, 2014,
我有这个架构。它检查评论,目前工作正常。 var schema = { id: '', type: 'object', additionalProperties: false, pro
这可能很简单,但有人可以解释为什么以下模式匹配不明智吗?它说其他规则,例如1, 0, _ 永远不会匹配。 let matchTest(n : int) = let ran = new Rand
我有以下选择序列作为 XML 模式的一部分。理想情况下,我想要一个序列: 来自 my:namespace 的元素必须严格解析。 来自任何其他命名空间的元素,不包括 ##targetNamespace和
我希望编写一个 json 模式来涵盖这个(简化的)示例 { "errorMessage": "", "nbRunningQueries": 0, "isError": Fals
首先,我是 f# 的新手,所以也许答案很明显,但我没有看到。所以我有一些带有 id 和值的元组。我知道我正在寻找的 id,我想从我传入的三个元组中选择正确的元组。我打算用两个 match 语句来做到这
我是一名优秀的程序员,十分优秀!