package com.reindeercrafts.notificationpeek.peek;
import android.app.Activity;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.service.notification.StatusBarNotification;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.GridLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.reindeercrafts.notificationpeek.NotificationHub;
import com.reindeercrafts.notificationpeek.R;
import com.reindeercrafts.notificationpeek.settings.PreferenceKeys;
import com.reindeercrafts.notificationpeek.settings.appearance.WallpaperFactory;
import com.reindeercrafts.notificationpeek.utils.NotificationPeekViewUtils;
import java.util.Date;
/**
* This class is used to display the Notification Peek layout. The original implementation
* uses Window and directly added layout to it, but it seems impossible to do so externally.
* <p/>
* This class also controls waking up the device and removing Peek layout from its parent.
*/
public class NotificationPeekActivity extends Activity {
private static final String TAG = NotificationPeekActivity.class.getSimpleName();
private static final long LOCK_DELAY = 200; // 200 ms
private TextView mClockTextView;
private NotificationPeekReceiver mReceiver;
private RelativeLayout mPeekView;
private NotificationLayout mNotificationLayout;
private GridLayout mNotificationsContainer;
private ViewGroup mNotificationView;
private ImageView mNotificationIcon;
private TextView mNotificationText;
private NotificationPeek mPeek;
@Override
protected void onCreate(Bundle savedInstanceState) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
);
if (WallpaperFactory.isLiveWallpaperUsed(this) &&
WallpaperFactory.isWallpaperThemeSelected(this)) {
setTheme(R.style.AppTheme_Peek_Wallpaper);
} else {
setTheme(R.style.AppTheme_Peek);
}
super.onCreate(savedInstanceState);
mPeekView = NotificationPeek.getPeekView();
mNotificationsContainer = (GridLayout) mPeekView.findViewById(R.id.notification_container);
mNotificationView = (ViewGroup) mPeekView.findViewById(R.id.notification_view);
setContentView(mPeekView);
boolean showClock = PreferenceManager.getDefaultSharedPreferences(this)
.getBoolean(PreferenceKeys.PREF_CLOCK, true);
if (showClock) {
mClockTextView = (TextView) PeekLayoutFactory
.createPeekLayout(this, PeekLayoutFactory.LAYOUT_TYPE_CLOCK, null);
mPeekView.addView(mClockTextView);
mClockTextView.setText(getCurrentTimeText());
}
mPeekView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_IMMERSIVE |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
);
// Initialize broadcast receiver.
initReceiver();
// Retrieve NotificationPeek object.
mNotificationLayout = (NotificationLayout) mPeekView.findViewById(R.id.notification_layout);
mPeek = mNotificationLayout.getNotificationPeek();
// Setup OnClickListener.
mNotificationIcon = (ImageView) mPeekView.findViewById(R.id.notification_icon);
// Notification snippet TextView.
mNotificationText = (TextView) mPeekView.findViewById(R.id.notification_text);
}
private void initReceiver() {
mReceiver = new NotificationPeekReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(NotificationPeekReceiver.ACTION_FINISH_PEEK);
filter.addAction(NotificationPeekReceiver.ACTION_DIMISS_NOTIFICATION);
filter.addAction(NotificationPeekReceiver.ACTION_UPDATE_NOTIFICATION_ICONS);
filter.addAction(NotificationPeekReceiver.ACTION_SHOW_CONTENT);
filter.addAction(NotificationPeekReceiver.ACTION_HIDE_CONTENT);
// Add time tick intent only when the clock is shown.
if (mClockTextView != null) {
filter.addAction(Intent.ACTION_TIME_TICK);
}
registerReceiver(mReceiver, filter);
}
@Override
protected void onDestroy() {
super.onDestroy();
// Remove mPeekView from its parent so that it can be reused.
ViewGroup parent = (ViewGroup) mPeekView.getParent();
parent.removeView(mPeekView);
try {
// Remove Clock only when it is displayed.
if (mClockTextView != null) {
mPeekView.removeView(mClockTextView);
}
unregisterReceiver(mReceiver);
} catch (Exception e) {
e.printStackTrace();
}
mPeekView = null;
}
/**
* Get formatted time String (Follows system setting).
*
* @return Formatted time String.
*/
private String getCurrentTimeText() {
return android.text.format.DateFormat.getTimeFormat(this).format(new Date());
}
/**
* Update notification views upon each swipe, moving the next latest notification to 'Current
* Notification' spot, and remove it from the small icon container.
*
* @param description Description of the StatusBarNotification we just swiped.
* @return True if the update is successful, i.e there is more than one unread notification.
* False if the notification we just swipe away is the last unread notification.
*/
private boolean updateNotification(String description) {
int currentNotificationIdex =
getCurrentNotificationIndex(mNotificationsContainer, description);
// Remove the current notification from container.
mNotificationsContainer.removeViewAt(currentNotificationIdex);
int nextNotificationIndex = mNotificationsContainer.getChildCount() - 1;
// We have more than one unread notification.
if (nextNotificationIndex >= 0) {
StatusBarNotification nextNotification = (StatusBarNotification) mNotificationsContainer
.getChildAt(nextNotificationIndex).getTag();
if (nextNotification.getNotification().largeIcon != null) {
mNotificationIcon.setImageDrawable(NotificationPeekViewUtils
.getRoundedShape(getResources(),
nextNotification.getNotification().largeIcon));
} else {
mNotificationIcon.setImageDrawable(
NotificationPeekViewUtils.getIconFromResource(this, nextNotification));
}
mNotificationText.setText(
NotificationPeekViewUtils.getNotificationDisplayText(this, nextNotification));
// Animate back icon and text.
mNotificationView.setTranslationX(0);
mNotificationView.animate().alpha(1f).start();
// Set new tag.
mNotificationView.setTag(nextNotification);
mPeek.getNotificationHub().setCurrentNotification(nextNotification);
final PendingIntent contentIntent = nextNotification.getNotification().contentIntent;
if (contentIntent != null) {
NotificationClicker mNotificationClicker =
new NotificationClicker(this, contentIntent, mPeek);
mNotificationIcon.setOnClickListener(mNotificationClicker);
} else {
mNotificationIcon.setOnClickListener(null);
}
if (nextNotificationIndex == 0) {
// As we already moved the next notification to 'Current Notification' spot, we need
// to hide it too if there is only one unread notification left.
mNotificationsContainer.getChildAt(nextNotificationIndex).setVisibility(View.GONE);
} else {
// Otherwise, highlight that icon.
mNotificationsContainer.getChildAt(nextNotificationIndex).setAlpha(1);
}
// Recover TextView alpha, because we will still have notification(s) to show.
mPeek.updateNotificationTextAlpha(1);
return true;
}
// The only unread notification is swiped away.
return false;
}
private int getCurrentNotificationIndex(ViewGroup container, String description) {
for (int i = 0; i < container.getChildCount(); i++) {
StatusBarNotification n = (StatusBarNotification) container.getChildAt(i).getTag();
if (NotificationHelper.getContentDescription(n).equals(description)) {
return i;
}
}
return -1;
}
private void restoreFirstIconVisibility() {
View firstIcon = mNotificationsContainer.getChildAt(0);
if (firstIcon.getVisibility() == View.GONE) {
// The first icon is hidden before in response to the swipe gesture,
firstIcon.setVisibility(View.VISIBLE);
}
// Highlight the first notification if it is currently selected.
StatusBarNotification largeIconNotification =
(StatusBarNotification) mNotificationView.getTag();
StatusBarNotification firstIconNotification = (StatusBarNotification) firstIcon.getTag();
if (largeIconNotification.getPackageName().equals(firstIconNotification.getPackageName())) {
firstIcon.setAlpha(1f);
}
}
/**
* Check whether a notification with the same package name as the new notification is
* shown in the icon container.
*
* @param hub NotificationHub instance.
* @return Index of the icon ImageView in its parent. -1 if not found.
*/
private int getOldIconViewIndex(NotificationHub hub) {
for (int i = 0; i < mNotificationsContainer.getChildCount(); i++) {
View child = mNotificationsContainer.getChildAt(i);
if (child.getTag() == null) {
continue;
}
StatusBarNotification n = (StatusBarNotification) child.getTag();
if (n.getPackageName().equals(hub.getCurrentNotification().getPackageName())) {
return i;
}
}
return -1;
}
/**
* Update small notification icons when there is new notification coming, and the
* Activity is in foreground.
*/
private void updateNotificationIcons() {
if (mNotificationsContainer.getVisibility() != View.VISIBLE) {
mNotificationsContainer.setVisibility(View.VISIBLE);
}
NotificationHub notificationHub = NotificationHub.getInstance();
int iconSize = getResources().getDimensionPixelSize(R.dimen.small_notification_icon_size);
int padding = getResources().getDimensionPixelSize(R.dimen.small_notification_icon_padding);
final StatusBarNotification n = notificationHub.getCurrentNotification();
ImageView icon = new ImageView(this);
icon.setAlpha(NotificationPeek.ICON_LOW_OPACITY);
icon.setPadding(padding, 0, padding, 0);
icon.setImageDrawable(NotificationPeekViewUtils.getIconFromResource(this, n));
icon.setTag(n);
restoreFirstIconVisibility();
int oldIndex = getOldIconViewIndex(notificationHub);
if (oldIndex >= 0) {
mNotificationsContainer.removeViewAt(oldIndex);
}
mNotificationsContainer.addView(icon);
LinearLayout.LayoutParams linearLayoutParams =
new LinearLayout.LayoutParams(iconSize, iconSize);
// Wrap LayoutParams to GridLayout.LayoutParams.
GridLayout.LayoutParams gridLayoutParams = new GridLayout.LayoutParams(linearLayoutParams);
icon.setLayoutParams(gridLayoutParams);
}
/**
* Animate the small TextView below the icon and the clock TextView and hide them when
* the notification content is displayed.
*/
private void hidePeekComponents() {
mNotificationText.animate().alpha(0f).setInterpolator(new DecelerateInterpolator()).start();
if (mClockTextView != null) {
mClockTextView.animate().alpha(0f).setInterpolator(new DecelerateInterpolator())
.start();
}
}
/**
* Bring the small TextView and the clock TextView back when the notification content
* is removed.
*/
private void showPeekComponents() {
mNotificationText.animate().alpha(1f).setInterpolator(new AccelerateInterpolator()).start();
if (mClockTextView != null) {
mClockTextView.animate().alpha(1f).setInterpolator(new AccelerateInterpolator())
.start();
}
}
/**
* Release partial wake lock from NotificationPeek object and
* lock screen.
*/
private void lockScreen() {
// Lock screen by a fixed delay, otherwise sometimes the screen will turn on again
// showing the lock screen UI.
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
// Release partial wake lock.
mPeek.dismissNotification();
DevicePolicyManager devicePolicyManager =
(DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
devicePolicyManager.lockNow();
}
}, LOCK_DELAY);
}
public class NotificationPeekReceiver extends BroadcastReceiver {
// Action for dismissing notification peek view.
public static final String ACTION_DIMISS_NOTIFICATION =
"NotificationPeek.dismiss_notification";
// Action for updating notification icons.
public static final String ACTION_UPDATE_NOTIFICATION_ICONS =
"NotificationPeek.update_notification";
// Action for finishing this activity.
public static final String ACTION_FINISH_PEEK = "NotificationPeek.finish_peek";
public static final String ACTION_SHOW_CONTENT = "NotificationPeek.show_content";
public static final String ACTION_HIDE_CONTENT = "NotificationPeek.hide_content";
public static final String EXTRA_NOTIFICATION_DESCRIPTION =
"NotificationPeek.extra_status_bar_notification_description";
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_FINISH_PEEK)) {
finish();
} else if (intent.getAction().equals(ACTION_DIMISS_NOTIFICATION)) {
String description = intent.getStringExtra(EXTRA_NOTIFICATION_DESCRIPTION);
if (!updateNotification(description)) {
lockScreen();
}
} else if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
mClockTextView.setText(getCurrentTimeText());
} else if (intent.getAction().equals(ACTION_UPDATE_NOTIFICATION_ICONS)) {
// Update notification icons according to actions.
updateNotificationIcons();
} else if (intent.getAction().equals(ACTION_SHOW_CONTENT)) {
// Display current notification's content.
hidePeekComponents();
} else if (intent.getAction().equals(ACTION_HIDE_CONTENT)) {
// Hide notification content view.
showPeekComponents();
}
}
}
}