需求:控制Notification显示,让所有的通知都没有声音、震动、headsup(就是弹出来显示,类似短信)。但是在statusbar下拉时还有这个通知,这样玩游戏的时候就不会受到影响,通知也不会丢。

其实这个功能Android7.0默认就有了,在Settings中。

这里如果选择Block all就会阻止这个app相关的所有通知。不会有任何通知。如果选择Show silently,就和我们的需求是一致的。这里的实现代码已经找到了,但是我们不能用,因为用了的话就会产生冲突。比如我没有选Show silently,当我们这个需求的模式开启后,在进Settings这个选项看的话,肯定就会被勾选了,所以我们不能用它的方法实现。

这个Show silently的实现代码就是在frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java

    void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);// Now, cancel any outstanding notifications that are part of a just-disabled appif (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),REASON_PACKAGE_BANNED, null);}}

我们这里不用这个方法,我们要看这个方法设置了之后,哪里会用到,哪里会判断,来控制通知的显示,到时候就在那里加我们的判断来实现控制通知。就是多加一个我们的flag而已。


这边的通知是分2部分的,一部分是控制通知的声音、震动、led灯这些,还有一部分就是statusbar上的显示,比如是不是弹出显示通知Headsup。

1.NotificationManagerService控制通知声音、震动等,然后丢给StatusBar显示。
当应用程序发出通知,就一定会走这里

private class EnqueueNotificationRunnable implements Runnable {private final NotificationRecord r;private final int userId;EnqueueNotificationRunnable(int userId, NotificationRecord r) {this.userId = userId;this.r = r;};@Overridepublic void run() {synchronized (mNotificationList) {final StatusBarNotification n = r.sbn;if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());NotificationRecord old = mNotificationsByKey.get(n.getKey());if (old != null) {// Retain ranking information from previous recordr.copyRankingInformation(old);Log.i("a", "NotificationManagerService: 2652");}final int callingUid = n.getUid();final int callingPid = n.getInitialPid();final Notification notification = n.getNotification();final String pkg = n.getPackageName();final int id = n.getId();final String tag = n.getTag();final boolean isSystemNotification = isUidSystem(callingUid) ||("android".equals(pkg));// Handle grouped notifications and bail out early if we// can to avoid extracting signals.handleGroupedNotificationLocked(r, old, callingUid, callingPid);// This conditional is a dirty hack to limit the logging done on//     behalf of the download manager without affecting other apps.if (!pkg.equals("com.android.providers.downloads")|| Log.isLoggable("DownloadManager", Log.VERBOSE)) {int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;if (old != null) {enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;Log.i("a", "NotificationManagerService: 2676");}EventLogTags.writeNotificationEnqueue(callingUid, callingPid,pkg, id, tag, userId, notification.toString(),enqueueStatus);}mRankingHelper.extractSignals(r);final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);// blocked apps// 这里是blocked的apps,就是你在settings中选择Block all的app,这里就会帮通知都屏蔽了if (r.getImportance() == NotificationListenerService.Ranking.IMPORTANCE_NONE|| !noteNotificationOp(pkg, callingUid) || isPackageSuspended) {if (!isSystemNotification) {if (isPackageSuspended) {Slog.e(TAG, "Suppressing notification from package due to package "+ "suspended by administrator.");mUsageStats.registerSuspendedByAdmin(r);Log.i("a", "NotificationManagerService: 2695");} else {Slog.e(TAG, "Suppressing notification from package by user request.");mUsageStats.registerBlocked(r);Log.i("a", "NotificationManagerService: 2699");}return;}}// tell the ranker service about the notificationif (mRankerServices.isEnabled()) {mRankerServices.onNotificationEnqueued(r);Log.i("a", "NotificationManagerService: 2708");// TODO delay the code below here for 100ms or until there is an answer}int index = indexOfNotificationLocked(n.getKey());if (index < 0) {mNotificationList.add(r);mUsageStats.registerPostedByApp(r);Log.i("a", "NotificationManagerService: 2717");} else {old = mNotificationList.get(index);mNotificationList.set(index, r);mUsageStats.registerUpdatedByApp(r, old);// Make sure we don't lose the foreground service state.notification.flags |=old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;r.isUpdate = true;Log.i("a", "NotificationManagerService: 2726");}mNotificationsByKey.put(n.getKey(), r);// Ensure if this is a foreground service that the proper additional// flags are set.if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {notification.flags |= Notification.FLAG_ONGOING_EVENT| Notification.FLAG_NO_CLEAR;Log.i("a", "NotificationManagerService: 2736");}applyZenModeLocked(r);mRankingHelper.sort(mNotificationList);if (notification.getSmallIcon() != null) {StatusBarNotification oldSbn = (old != null) ? old.sbn : null;mListeners.notifyPostedLocked(n, oldSbn);Log.i("a", "NotificationManagerService: 2745");} else {Slog.e(TAG, "Not posting notification without small icon: " + notification);if (old != null && !old.isCanceled) {mListeners.notifyRemovedLocked(n);Log.i("a", "NotificationManagerService: 2750");}// ATTENTION: in a future release we will bail out here// so that we do not play sounds, show lights, etc. for invalid// notificationsSlog.e(TAG, "WARNING: In a future release this will crash the app: "+ n.getPackageName());}buzzBeepBlinkLocked(r);}}}

下面来看看buzzBeepBlinkLocked这个函数

@VisibleForTestingvoid buzzBeepBlinkLocked(NotificationRecord record) {boolean buzz = false;boolean beep = false;boolean blink = false;final Notification notification = record.sbn.getNotification();final String key = record.getKey();// Should this notification make noise, vibe, or use the LED?final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_DEFAULT;final boolean canInterrupt = aboveThreshold && !record.isIntercepted();if (DBG || record.isIntercepted())Slog.v(TAG,"pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +" intercept=" + record.isIntercepted());final int currentUser;final long token = Binder.clearCallingIdentity();try {currentUser = ActivityManager.getCurrentUser();} finally {Binder.restoreCallingIdentity(token);}Log.i("a", "NotificationManagerService: 2837");// If we're not supposed to beep, vibrate, etc. then don't.final String disableEffects = disableNotificationEffects(record);if (disableEffects != null) {ZenLog.traceDisableEffects(record, disableEffects);Log.i("a", "NotificationManagerService: 2843");}// Remember if this notification already owns the notification channels.boolean wasBeep = key != null && key.equals(mSoundNotificationKey);boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);// These are set inside the conditional if the notification is allowed to make noise.boolean hasValidVibrate = false;boolean hasValidSound = false;boolean smsRingtone =  false;if (mCarrierConfig != null) {smsRingtone = mCarrierConfig.getBoolean(CarrierConfigManager.KEY_CONFIG_SMS_RINGTONE_INCALL);Log.i("a", "NotificationManagerService: 2858");}Log.i("a", "NotificationManagerService: 2860");// 这里就是我们要加flag判断的地方。如果要silent通知就不要进这个if判断,我们可以在这个if外面在多加一个我们的flag。if ((disableEffects == null || (smsRingtone && mInCall))&& (record.getUserId() == UserHandle.USER_ALL ||record.getUserId() == currentUser ||mUserProfiles.isCurrentProfile(record.getUserId()))&& canInterrupt&& mSystemReady&& mAudioManager != null) {if (DBG) Slog.v(TAG, "Interrupting!");Log.i("a", "NotificationManagerService: 2869");// should we use the default notification sound? (indicated either by// DEFAULT_SOUND or because notification.sound is pointing at// Settings.System.NOTIFICATION_SOUND)final boolean useDefaultSound =(notification.defaults & Notification.DEFAULT_SOUND) != 0 ||Settings.System.DEFAULT_NOTIFICATION_URI.equals(notification.sound);Uri soundUri = null;if (useDefaultSound) {soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;// check to see if the default notification sound is silenthasValidSound = mSystemNotificationSound != null;Log.i("a", "NotificationManagerService: 2884");} else if (notification.sound != null) {soundUri = notification.sound;hasValidSound = (soundUri != null);Log.i("a", "NotificationManagerService: 2888");}// Does the notification want to specify its own vibration?final boolean hasCustomVibrate = notification.vibrate != null;// new in 4.2: if there was supposed to be a sound and we're in vibrate// mode, and no other vibration is specified, we fall back to vibrationfinal boolean convertSoundToVibration =!hasCustomVibrate&& hasValidSound&& (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE);// The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.final boolean useDefaultVibrate =(notification.defaults & Notification.DEFAULT_VIBRATE) != 0;hasValidVibrate = useDefaultVibrate || convertSoundToVibration ||hasCustomVibrate;Log.i("a", "useDefaultVibrate || convertSoundToVibration hasCustomVibrate  "+useDefaultVibrate+convertSoundToVibration+hasCustomVibrate);// We can alert, and we're allowed to alert, but if the developer asked us to only do// it once, and we already have, then don't.if (!(record.isUpdate&& (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)) {sendAccessibilityEvent(notification, record.sbn.getPackageName());if (hasValidSound) {boolean looping =(notification.flags & Notification.FLAG_INSISTENT) != 0;AudioAttributes audioAttributes = audioAttributesForNotification(notification);mSoundNotificationKey = key;// do not play notifications if stream volume is 0 (typically because// ringer mode is silent) or if there is a user of exclusive audio focusif ((mAudioManager.getStreamVolume(AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)&& !mAudioManager.isAudioFocusExclusive()) {final long identity = Binder.clearCallingIdentity();try {final IRingtonePlayer player =mAudioManager.getRingtonePlayer();if (player != null) {if (DBG) Slog.v(TAG, "Playing sound " + soundUri+ " with attributes " + audioAttributes);player.playAsync(soundUri, record.sbn.getUser(), looping,audioAttributes);beep = true;}} catch (RemoteException e) {} finally {Binder.restoreCallingIdentity(identity);}}}if (hasValidVibrate && !(mAudioManager.getRingerModeInternal()== AudioManager.RINGER_MODE_SILENT)) {mVibrateNotificationKey = key;if (useDefaultVibrate || convertSoundToVibration) {// Escalate privileges so we can use the vibrator even if the// notifying app does not have the VIBRATE permission.long identity = Binder.clearCallingIdentity();try {mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),useDefaultVibrate ? mDefaultVibrationPattern: mFallbackVibrationPattern,((notification.flags & Notification.FLAG_INSISTENT) != 0)? 0: -1, audioAttributesForNotification(notification));buzz = true;} finally {Binder.restoreCallingIdentity(identity);}} else if (notification.vibrate.length > 1) {// If you want your own vibration pattern, you need the VIBRATE// permissionmVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),notification.vibrate,((notification.flags & Notification.FLAG_INSISTENT) != 0)? 0: -1, audioAttributesForNotification(notification));buzz = true;}}}}Log.i("a", "NotificationManagerService: 2977");// If a notification is updated to remove the actively playing sound or vibrate,// cancel that feedback nowif (wasBeep && !hasValidSound) {clearSoundLocked();Log.i("a", "NotificationManagerService: 2982");}if (wasBuzz && !hasValidVibrate) {clearVibrateLocked();Log.i("a", "NotificationManagerService: 2986");}// light// release the lightboolean wasShowLights = mLights.remove(key);if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold&& ((record.getSuppressedVisualEffects()& NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {Log.i("a", "NotificationManagerService: 2995");mLights.add(key);updateLightsLocked();if (mUseAttentionLight) {mAttentionLight.pulse();}blink = true;} else if (wasShowLights) {updateLightsLocked();Log.i("a", "NotificationManagerService: 3004");}if (buzz || beep || blink) {if (((record.getSuppressedVisualEffects()& NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) != 0)) {if (DBG) Slog.v(TAG, "Suppressed SystemUI from triggering screen on");} else {EventLogTags.writeNotificationAlert(key,buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);mHandler.post(mBuzzBeepBlinked);}}}

关于声音、震动、灯光就在上面这处理的。

2.StatusBar部分,控制通知的显示,Headsup就在这控制。
BaseStatusBar.java
当有通知从NotificationManagerService过来后,就会走onNotificationPosted。onNotificationPosted会根据通知是新增还是更新做不同的处理,所以我们要在2个地方去阻止通知显示。

@Overridepublic void onNotificationPosted(final StatusBarNotification sbn,final RankingMap rankingMap) {if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);Log.i("a", "StatusBarNotification "+sbn);Log.i("a", "Notification "+sbn.getNotification());if (sbn != null) {mHandler.post(new Runnable() {@Overridepublic void run() {processForRemoteInput(sbn.getNotification());String key = sbn.getKey();mKeysKeptForRemoteInput.remove(key);boolean isUpdate = mNotificationData.get(key) != null;// In case we don't allow child notifications, we ignore children of// notifications that have a summary, since we're not going to show them// anyway. This is true also when the summary is canceled,// because children are automatically canceled by NoMan in that case.if (!ENABLE_CHILD_NOTIFICATIONS&& mGroupManager.isChildInGroupWithSummary(sbn)) {if (DEBUG) {Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);}// Remove existing notification to avoid stale data.if (isUpdate) {removeNotification(key, rankingMap);} else {mNotificationData.updateRanking(rankingMap);}return;}if (isUpdate) {updateNotification(sbn, rankingMap);Log.i("a", "StatusBar:  "+653);} else {addNotification(sbn, rankingMap, null /* oldEntry */);Log.i("a", "StatusBar:  "+656);}}});}}

首先看updateNotification,这个函数在BaseStatusBar.java中

public void updateNotification(StatusBarNotification notification, RankingMap ranking) {if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");final String key = notification.getKey();Entry entry = mNotificationData.get(key);if (entry == null) {return;} else {mHeadsUpEntriesToRemoveOnSwitch.remove(entry);mRemoteInputEntriesToRemoveOnCollapse.remove(entry);}Notification n = notification.getNotification();mNotificationData.updateRanking(ranking);boolean applyInPlace;try {applyInPlace = entry.cacheContentViews(mContext, notification.getNotification());} catch (RuntimeException e) {Log.e(TAG, "Unable to get notification remote views", e);applyInPlace = false;}boolean shouldPeek = shouldPeek(entry, notification);//这里会根据shouldPeek的值,来处理是否显示headsup通知,所以把shouldPeek该成false就可以阻止了。Log.i("a", "StatusBar: set shouldPeek updateNotification  "+shouldPeek);Log.i("a", "StatusBar:  "+2407);boolean alertAgain = alertAgain(entry, n);if (DEBUG) {Log.d(TAG, "applyInPlace=" + applyInPlace+ " shouldPeek=" + shouldPeek+ " alertAgain=" + alertAgain);}final StatusBarNotification oldNotification = entry.notification;entry.notification = notification;mGroupManager.onEntryUpdated(entry, oldNotification);boolean updateSuccessful = false;if (applyInPlace) {Log.i("a", "StatusBar:  "+2421);if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);try {if (entry.icon != null) {Log.i("a", "StatusBar:  "+2425);// Update the iconfinal StatusBarIcon ic = new StatusBarIcon(notification.getUser(),notification.getPackageName(),n.getSmallIcon(),n.iconLevel,n.number,StatusBarIconView.contentDescForNotification(mContext, n));entry.icon.setNotification(n);if (!entry.icon.set(ic)) {Log.i("a", "StatusBar:  "+2436);handleNotificationError(notification, "Couldn't update icon: " + ic);return;}}Log.i("a", "StatusBar:  "+2441);updateNotificationViews(entry, notification);updateSuccessful = true;}catch (RuntimeException e) {// It failed to apply cleanly.Log.w(TAG, "Couldn't reapply views for package " +notification.getPackageName(), e);}}if (!updateSuccessful) {Log.i("a", "StatusBar:  "+2452);if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);final StatusBarIcon ic = new StatusBarIcon(notification.getUser(),notification.getPackageName(),n.getSmallIcon(),n.iconLevel,n.number,StatusBarIconView.contentDescForNotification(mContext, n));entry.icon.setNotification(n);entry.icon.set(ic);if (!inflateViews(entry, mStackScroller)) {Log.i("a", "StatusBar:  "+2464);handleNotificationError(notification, "Couldn't update remote views for: "+ notification);}}updateHeadsUp(key, entry, shouldPeek, alertAgain);updateNotifications();if (!notification.isClearable()) {Log.i("a", "StatusBar:  "+2473);// The user may have performed a dismiss action on the notification, since it's// not clearable we should snap it back.mStackScroller.snapViewIfNeeded(entry.row);}if (DEBUG) {// Is this for you?boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");}setAreThereNotifications();}

addNotification是在PhoneStatusBar.java中实现的。

@Overridepublic void addNotification(StatusBarNotification notification, RankingMap ranking,Entry oldEntry) {if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey());mNotificationData.updateRanking(ranking);Entry shadeEntry = createNotificationViews(notification);if (shadeEntry == null) {return;}boolean isHeadsUped = shouldPeek(shadeEntry);// 和updateNotification那一样,把isHeadsUped设置成false就可以阻止了。Log.i("a", "PhoneStatusBar: set is headsuped false");Log.i("a", "PhoneStatusBar:  "+isHeadsUped+1575);if (isHeadsUped) {mHeadsUpManager.showNotification(shadeEntry);// Mark as seen immediatelysetNotificationShown(notification);Log.i("a", "PhoneStatusBar:  "+1582);}if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {if (shouldSuppressFullScreenIntent(notification.getKey())) {if (DEBUG) {Log.i("a", "PhoneStatusBar:  "+1586);Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + notification.getKey());}} else if (mNotificationData.getImportance(notification.getKey())< NotificationListenerService.Ranking.IMPORTANCE_MAX) {if (DEBUG) {Log.i("a", "PhoneStatusBar:  "+1592);Log.d(TAG, "No Fullscreen intent: not important enough: "+ notification.getKey());}}else if(!isHeadsUped) {Log.i("a", "PhoneStatusBar:  "+1599);}else {Log.i("a", "PhoneStatusBar:  "+1597);// Stop screensaver if the notification has a full-screen intent.// (like an incoming phone call)awakenDreams();// not immersive & a full-screen alert should be shownif (DEBUG)Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");try {EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,notification.getKey());notification.getNotification().fullScreenIntent.send();shadeEntry.notifyFullScreenIntentLaunched();MetricsLogger.count(mContext, "note_fullscreen", 1);} catch (PendingIntent.CanceledException e) {}}}addNotificationViews(shadeEntry, ranking);// Recalculate the position of the sliding windows and the titles.setAreThereNotifications();}

最后看看shouldPeek,在BaseStatusBar.java中实现

protected boolean shouldPeek(Entry entry, StatusBarNotification sbn) {Log.i("a", "StatusBar:  "+2548);if (!mUseHeadsUp || isDeviceInVrMode()) {Log.i("a", "StatusBar:  "+2550);return false;}if (mNotificationData.shouldFilterOut(sbn)) {if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());Log.i("a", "StatusBar:  "+2556);return false;}boolean inUse = mPowerManager.isScreenOn();try {inUse = inUse && !mDreamManager.isDreaming();Log.i("a", "StatusBar:  "+2563);} catch (RemoteException e) {Log.d(TAG, "failed to query dream manager", e);}if (!inUse) {if (DEBUG) {Log.d(TAG, "No peeking: not in use: " + sbn.getKey());}Log.i("a", "StatusBar:  "+2572);return false;}if (mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());Log.i("a", "StatusBar:  "+2578);return false;}if (entry.hasJustLaunchedFullScreenIntent()) {if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());Log.i("a", "StatusBar:  "+2584);return false;}if (isSnoozedPackage(sbn)) {if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());Log.i("a", "StatusBar:  "+2590);return false;}// 在settings中设置了show silently后,就会走这里。只要这个shouldPeek函数返回false,就不会显示headsup这样的通知。if (mNotificationData.getImportance(sbn.getKey()) < IMPORTANCE_HIGH) {if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());Log.i("a", "StatusBar:  "+2596);return false;}if (sbn.getNotification().fullScreenIntent != null) {if (mAccessibilityManager.isTouchExplorationEnabled()) {if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());Log.i("a", "StatusBar:  "+2603);return false;} else {Log.i("a", "StatusBar:  "+2606);// we only allow head-up on the lockscreen if it doesn't have a fullscreen intentreturn !mStatusBarKeyguardViewManager.isShowing()|| mStatusBarKeyguardViewManager.isOccluded();}}return true;}

最后在settings数据库中加入自己的字段做flag,在上面的地方来判断,是否开启这个模式。开启后就可以实现和系统自带的Settings->Notifications中Show silently一样的效果了,也不会和系统这个功能有冲突。

Android7.0 Notification Show silently 阻止通知 定制相关推荐

  1. nougat什么时候发布的_如何在Android Nougat中管理,自定义和阻止通知

    nougat什么时候发布的 Android 7.0 Nougat made some pretty big improvements to notifications, but there's one ...

  2. Android7.0新特性、新功能

    [本文转载来自http://blog.csdn.net/hao54216/article/details/52388755] 前言: 总想写点自己的东西,因为很多Android知识网上大部分都有教程, ...

  3. Android7.0适配

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 转载请注明出处:http://blog.csdn.net/chay_chan/article/details/57083383 关于 ...

  4. Android7.0的适配

    关于Android7.0的适配   最近在软件的维护和更新过程中,了解到一些关于Android7.0的适配,在这里和大家分享一下,据我所知,需要对Notification.拍照.图片的裁剪进行适配 一 ...

  5. Android7.0 数据拨号前的准备工作

    背景  在介绍PhoneApp的创建过程时,我们知道为了支持双卡手机,PhoneFactory创建了两个Phone对象.  然而由于通信制式.功耗等的限制,目前底层的芯片厂商规定modem工作于DSD ...

  6. Android7.0 数据拨号前的准备工作

    背景 在介绍PhoneApp的创建过程时,我们知道为了支持双卡手机,PhoneFactory创建了两个Phone对象. 然而由于通信制式.功耗等的限制,目前底层的芯片厂商规定modem工作于DSDS模 ...

  7. android7虚拟定位,Android8.0还早 EMUI助推Android7.0普及

    5月17日至19日,谷歌2017年开发者大会在加州谷歌总部所在地举办.本次会议当中,最重要产品之一即新一代Android O操作系统(也就是我们所说Android 8.0)成为最大的亮点.最新发布的A ...

  8. Android7.0 Rild工作流程

    点击打开链接 一.基于Rild的通信架构 一般智能手机的硬件架构都是两个处理器:  一个处理器用来运行操作系统,上面运行应用程序,这个处理器称作Application Processor,简称AP:另 ...

  9. Android 4.0 Notification

    通常,在手机有未接电话,收到消息或者挂着退出主界面的QQ,在状态栏会有一个Notification,那么,这个notification如何产生的? 通常做法: Intent intent = new  ...

最新文章

  1. 两种方式(goto语句以及while循环)实现C语言关机小程序
  2. 多领导者改进算法的MATLAB仿真
  3. 如何判断数组所有数都不等于一个数_【每日算法Day 91】求解数组中出现次数超过1/3的那个数
  4. Understanding Clouds from Satellite Images的kernel调研+肉眼识别每种云朵示例
  5. 利用 CocoaLumberjack 搭建自己的 Log 系统
  6. 网站开发用什么语言好_网站开发教程:企业如何用网站开启在线业务?
  7. (pytorch-深度学习系列)读取和存储数据-学习笔记
  8. java saxexception_SAXException
  9. 修改Chrome默认搜索引擎
  10. 【百度地图API】情人节求爱大作战——添加标注功能
  11. 爱奇艺开播助手Flutter跨平台Hybrid实践\n
  12. 什么是URL Rewrite?URL Rewrite有什么用?
  13. 80386常用内部寄存器
  14. [Mysql] STR_TO_DATE函数
  15. C语言编程入门训练(一)
  16. 1080p和1080i有什么区别?
  17. 谈谈浏览器中富文本编辑器的技术演进
  18. 【Arduino实验08 红外传感器】
  19. [工具使用]搜索引擎 Hacking
  20. 计算机改显存会有啥影响,显卡显存越大越好吗?显存对电脑速度的影响有哪些?...

热门文章

  1. CQF笔记M1L3泰勒级数和转移概率密度函数
  2. android 省市区三级联动选择器
  3. 计算机毕业设计之Android的游戏账号交易平台APP(源码+系统+mysql数据库+Lw文档)
  4. 软件开发 过程管理流程设计
  5. ProcDump+Mimikatz绕过杀毒软件抓密码
  6. iOS10 配置ATS
  7. asp.net医院信息管理系统VS开发sqlserver数据库web结构c#编程计算机网页源码项目
  8. ArcGis配色心得
  9. 深刻认识 -- 立即数
  10. DL645通信协议97/07规约