Merge branch 'main_ui'

This commit is contained in:
danijoo
2016-01-02 23:31:31 +01:00
16 changed files with 527 additions and 36 deletions

View File

@@ -16,7 +16,7 @@
</intent-filter>
</activity>
<activity
android:name=".PreferenceActivity"
android:name=".preferences.PreferenceActivity"
android:parentActivityName=".MainActivity" />
<receiver
@@ -28,6 +28,7 @@
</receiver>
<service
android:label="@string/app_name"
android:name=".logger.NotificationLoggerService"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>

View File

@@ -15,6 +15,8 @@ import android.view.View;
import net.headlezz.notificationlogger.createnotification.NotificationCreationFragment;
import net.headlezz.notificationlogger.logger.LoggerUtils;
import net.headlezz.notificationlogger.notificationlist.NotificationListFragment;
import net.headlezz.notificationlogger.preferences.PreferenceActivity;
import net.headlezz.notificationlogger.presenter.LoggerWarningPresenter;
import butterknife.Bind;

View File

@@ -1,23 +0,0 @@
package net.headlezz.notificationlogger;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class NotificationListFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
TextView textView = new TextView(getContext());
textView.setGravity(Gravity.CENTER);
textView.setText(getClass().getSimpleName());
return textView;
}
}

View File

@@ -3,6 +3,10 @@ package net.headlezz.notificationlogger;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import timber.log.Timber;
public class PackageUtils {
@@ -12,5 +16,38 @@ public class PackageUtils {
return (String) pm.getApplicationLabel(appInfo);
}
/**
*
* @param context
* @param packageName
* @return Drawable or Null
*/
public static Drawable getApplicationLauncherIcon(Context context, String packageName) {
try {
return context.getPackageManager().getApplicationIcon(packageName);
} catch(PackageManager.NameNotFoundException e) {
Timber.w("package not found: " + packageName);
return null;
}
}
/**
*
* @param context
* @param packageName
* @param iconId
* @return Drawable or Null
*/
public static Drawable getApplicationDrawable(Context context, String packageName, int iconId) {
try {
Resources res = context.getPackageManager().getResourcesForApplication(packageName);
return res.getDrawable(iconId);
} catch (PackageManager.NameNotFoundException e) {
Timber.w("package not found: " + packageName);
return null;
}
}
}

View File

@@ -1,13 +1,13 @@
package net.headlezz.notificationlogger.logger;
import java.util.Date;
import ckm.simple.sql_provider.annotation.SimpleSQLColumn;
import ckm.simple.sql_provider.annotation.SimpleSQLTable;
@SimpleSQLTable(table="logged_notification", provider="NotificationProvider")
public class LoggedNotification {
@SimpleSQLColumn("_id")
public long id;
@SimpleSQLColumn("title")
public String title;
@@ -16,7 +16,7 @@ public class LoggedNotification {
public String message;
@SimpleSQLColumn("date")
public Date date;
public long date;
@SimpleSQLColumn("app_name")
public String appName;

View File

@@ -12,8 +12,6 @@ import android.service.notification.StatusBarNotification;
import net.headlezz.notificationlogger.PackageUtils;
import java.util.Date;
import timber.log.Timber;
public class NotificationLoggerService extends NotificationListenerService implements SharedPreferences.OnSharedPreferenceChangeListener {
@@ -69,7 +67,7 @@ public class NotificationLoggerService extends NotificationListenerService imple
int notificationId = sbn.getId();
int userId = sbn.getUserId();
Date date = new Date(sbn.getPostTime());
long date = sbn.getPostTime();
String packageName = sbn.getPackageName();
int iconId = sbn.getNotification().icon;

View File

@@ -0,0 +1,143 @@
package net.headlezz.notificationlogger.notificationlist;
/*
* Copyright (C) 2014 skyfish.jy@gmail.com
*
* 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.
*
*/
import android.content.Context;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.support.v7.widget.RecyclerView;
public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {
private Context mContext;
private Cursor mCursor;
private boolean mDataValid;
private int mRowIdColumn;
private DataSetObserver mDataSetObserver;
public CursorRecyclerViewAdapter(Context context, Cursor cursor) {
mContext = context;
mCursor = cursor;
mDataValid = cursor != null;
mRowIdColumn = mDataValid ? mCursor.getColumnIndex("_id") : -1;
mDataSetObserver = new NotifyingDataSetObserver();
if (mCursor != null) {
mCursor.registerDataSetObserver(mDataSetObserver);
}
}
public Cursor getCursor() {
return mCursor;
}
@Override
public int getItemCount() {
if (mDataValid && mCursor != null) {
return mCursor.getCount();
}
return 0;
}
@Override
public long getItemId(int position) {
if (mDataValid && mCursor != null && mCursor.moveToPosition(position)) {
return mCursor.getLong(mRowIdColumn);
}
return 0;
}
@Override
public void setHasStableIds(boolean hasStableIds) {
super.setHasStableIds(true);
}
public abstract void onBindViewHolder(VH viewHolder, Cursor cursor);
@Override
public void onBindViewHolder(VH viewHolder, int position) {
if (!mDataValid) {
throw new IllegalStateException("this should only be called when the cursor is valid");
}
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
onBindViewHolder(viewHolder, mCursor);
}
/**
* Change the underlying cursor to a new cursor. If there is an existing cursor it will be
* closed.
*/
public void changeCursor(Cursor cursor) {
Cursor old = swapCursor(cursor);
if (old != null) {
old.close();
}
}
/**
* Swap in a new Cursor, returning the old Cursor. Unlike
* {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
* closed.
*/
public Cursor swapCursor(Cursor newCursor) {
if (newCursor == mCursor) {
return null;
}
final Cursor oldCursor = mCursor;
if (oldCursor != null && mDataSetObserver != null) {
oldCursor.unregisterDataSetObserver(mDataSetObserver);
}
mCursor = newCursor;
if (mCursor != null) {
if (mDataSetObserver != null) {
mCursor.registerDataSetObserver(mDataSetObserver);
}
mRowIdColumn = newCursor.getColumnIndexOrThrow("_id");
mDataValid = true;
notifyDataSetChanged();
} else {
mRowIdColumn = -1;
mDataValid = false;
notifyDataSetChanged();
//There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
}
return oldCursor;
}
private class NotifyingDataSetObserver extends DataSetObserver {
@Override
public void onChanged() {
super.onChanged();
mDataValid = true;
notifyDataSetChanged();
}
@Override
public void onInvalidated() {
super.onInvalidated();
mDataValid = false;
notifyDataSetChanged();
//There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
}
}
}

View File

@@ -0,0 +1,110 @@
package net.headlezz.notificationlogger.notificationlist;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v7.widget.RecyclerView;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import net.headlezz.notificationlogger.PackageUtils;
import net.headlezz.notificationlogger.R;
import net.headlezz.notificationlogger.logger.LoggedNotification;
import net.headlezz.notificationlogger.logger.Logged_notificationTable;
import butterknife.Bind;
import butterknife.ButterKnife;
public class NotificationListAdapter extends CursorRecyclerViewAdapter<NotificationListAdapter.NotificationViewHolder> implements View.OnClickListener {
class NotificationViewHolder extends RecyclerView.ViewHolder {
@Bind(R.id.notification_item_message)
TextView tvMessage;
@Bind(R.id.notification_item_title)
TextView tvTitle;
@Bind(R.id.notification_item_appName)
TextView tvAppName;
@Bind(R.id.notification_item_date)
TextView tvDate;
@Bind(R.id.notification_item_appIcon)
ImageView ivAppIcon;
@Bind(R.id.notification_item_smallIcon)
ImageView ivSmallIcon;
public NotificationViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
public void setNotification(LoggedNotification n) {
Context context = itemView.getContext();
tvMessage.setText(n.message);
tvTitle.setText(n.title);
tvAppName.setText(n.appName);
CharSequence formattedDate = DateUtils.getRelativeTimeSpanString(n.date, System.currentTimeMillis(), DateUtils.SECOND_IN_MILLIS, DateUtils.FORMAT_ABBREV_ALL);
tvDate.setText(formattedDate);
Drawable appIcon = PackageUtils.getApplicationLauncherIcon(context, n.packageName);
ivAppIcon.setImageDrawable(appIcon);
ivAppIcon.setContentDescription(String.format(context.getString(R.string.app_icon_cd), n.appName));
Drawable smallIcon = DrawableCompat.wrap(PackageUtils.getApplicationDrawable(context, n.packageName, n.smallIconId));
DrawableCompat.setTint(smallIcon, Color.BLACK);
ivSmallIcon.setImageDrawable(smallIcon);
itemView.setTag(n.id);
}
}
interface NotificationClickListener {
void onNotificationClick(long id);
}
private NotificationClickListener mNotificationClickListener;
public NotificationListAdapter(Context context, Cursor cursor) {
super(context, cursor);
}
public void setNotificationClickListener(NotificationClickListener listener) {
mNotificationClickListener = listener;
}
@Override
public void onBindViewHolder(NotificationViewHolder viewHolder, Cursor cursor) {
LoggedNotification n = Logged_notificationTable.getRow(cursor, false);
viewHolder.setNotification(n);
}
@Override
public NotificationViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.notification_list_item, parent, false);
v.setOnClickListener(this);
return new NotificationViewHolder(v);
}
@Override
public void onClick(View v) {
if(mNotificationClickListener != null) {
long id = (long) v.getTag();
mNotificationClickListener.onNotificationClick(id);
}
}
}

View File

@@ -0,0 +1,100 @@
package net.headlezz.notificationlogger.notificationlist;
import android.database.Cursor;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import net.headlezz.notificationlogger.R;
import net.headlezz.notificationlogger.logger.LoggedNotification;
import net.headlezz.notificationlogger.logger.Logged_notificationTable;
import butterknife.Bind;
import butterknife.ButterKnife;
import timber.log.Timber;
public class NotificationListFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor>, NotificationListAdapter.NotificationClickListener {
final int LOADER_ID = 124;
@Bind(R.id.nList_emptyView)
View mEmptyView;
@Bind(R.id.nList_notificationList)
RecyclerView mNotificationList;
private NotificationListAdapter mAdapter;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.notification_list_fragment, container, false);
ButterKnife.bind(this, view);
mNotificationList.setLayoutManager(new LinearLayoutManager(getContext()));
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mAdapter = new NotificationListAdapter(getContext(), null);
mAdapter.setNotificationClickListener(this);
mNotificationList.setAdapter(mAdapter);
getLoaderManager().initLoader(LOADER_ID, Bundle.EMPTY, this);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Timber.d("Loader created.");
return new CursorLoader(
getContext(),
Logged_notificationTable.CONTENT_URI,
null,
null,
null,
Logged_notificationTable.FIELD_DATE + " DESC"
);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
Timber.d("Loader finished. " + data.getCount() + " items.");
mAdapter.changeCursor(data);
checkIfEmpty();
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
Timber.d("Loader reset.");
mAdapter.changeCursor(null);
checkIfEmpty();
}
private void checkIfEmpty() {
if(mAdapter.getItemCount() == 0) {
mEmptyView.setVisibility(View.VISIBLE);
mNotificationList.setVisibility(View.GONE);
} else {
mEmptyView.setVisibility(View.GONE);
mNotificationList.setVisibility(View.VISIBLE);
}
}
@Override
public void onNotificationClick(long id) {
String qry = Logged_notificationTable.FIELD__ID + " = ? ";
String[] args = { String.valueOf(id) };
Cursor cursor = getContext().getContentResolver().query(Logged_notificationTable.CONTENT_URI, null, qry, args, null);
LoggedNotification notification = Logged_notificationTable.getRow(cursor, true);
Timber.e(notification.title);
}
}

View File

@@ -1,4 +1,4 @@
package net.headlezz.notificationlogger;
package net.headlezz.notificationlogger.preferences;
import android.app.Activity;
import android.support.v4.app.Fragment;

View File

@@ -1,4 +1,4 @@
package net.headlezz.notificationlogger;
package net.headlezz.notificationlogger.preferences;
import android.os.Bundle;
import android.support.annotation.Nullable;
@@ -8,6 +8,8 @@ import android.support.v7.widget.Toolbar;
import com.mikepenz.aboutlibraries.LibsBuilder;
import com.mikepenz.aboutlibraries.ui.LibsSupportFragment;
import net.headlezz.notificationlogger.R;
import butterknife.Bind;
import butterknife.ButterKnife;

View File

@@ -1,4 +1,4 @@
package net.headlezz.notificationlogger;
package net.headlezz.notificationlogger.preferences;
import android.app.Activity;
import android.content.ContentResolver;
@@ -10,6 +10,7 @@ import android.support.v7.app.AlertDialog;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat;
import net.headlezz.notificationlogger.R;
import net.headlezz.notificationlogger.logger.Logged_notificationTable;
public class PreferenceFragment extends PreferenceFragmentCompat implements Preference.OnPreferenceClickListener {

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:clipToPadding="false"
android:paddingBottom="56dp"
android:visibility="gone"
tools:visibility="visible"
android:id="@+id/nList_notificationList"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:id="@+id/nList_emptyView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/notification_list_empty" />
</FrameLayout>

View File

@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="?attr/selectableItemBackground"
android:orientation="horizontal"
android:padding="8dp">
<ImageView
android:id="@+id/notification_item_appIcon"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginEnd="16dp"
tools:src="@mipmap/ic_launcher" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:orientation="vertical">
<ImageView
android:layout_marginEnd="4dp"
android:adjustViewBounds="true"
android:id="@+id/notification_item_smallIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:contentDescription="@string/notification_small_icon_cd"
tools:src="@drawable/ic_sd_card"
android:layout_above="@+id/notification_item_title"
android:layout_alignParentTop="true" />
<TextView
android:id="@+id/notification_item_appName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toEndOf="@+id/notification_item_smallIcon"
android:layout_toStartOf="@+id/notification_item_date"
android:ellipsize="end"
android:lines="1"
android:textAppearance="@style/TextAppearance.AppCompat.Title"
tools:text="A really long app name thats ellipsed" />
<TextView
android:id="@+id/notification_item_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@+id/notification_item_appName"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:lines="1"
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
tools:text="Notification title" />
<TextView
android:id="@+id/notification_item_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:layout_marginStart="4dp"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
tools:text="16 hours ago" />
<TextView
android:id="@+id/notification_item_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@+id/notification_item_title"
android:ellipsize="end"
android:maxLines="3"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
tools:text="Some messages wit lots of text and \nTwo lines" />
</RelativeLayout>
</LinearLayout>
<View
android:layout_gravity="bottom"
android:background="?attr/colorControlHighlight"
android:layout_height="1dp"
android:layout_width="match_parent"
/>
</FrameLayout>

View File

@@ -44,6 +44,9 @@
<string name="create_shedule_snack">Notification sheduled for %1$s</string>
<string name="create_shedule_snack_undo">Undo</string>
<string name="notification_list_empty">No notifications found.</string>
<string name="notification_small_icon_cd">Notification small icon</string>
<string name="app_icon_cd">Application icon for %s</string>
<string-array name="create_categories">
<item>Alarm</item>

View File

@@ -23,6 +23,6 @@ allprojects {
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
//task clean(type: Delete) {
// delete rootProject.buildDir
//}