[Widgets] Implement new Notifications widget.

This commit is contained in:
Kuba Szczodrzyński 2020-01-06 22:26:54 +01:00
parent 7b97ef316d
commit 878de34546
30 changed files with 457 additions and 603 deletions

View File

@ -25,10 +25,10 @@
-keep class pl.szczodrzynski.edziennik.data.db.entity.Event { *; }
-keep class pl.szczodrzynski.edziennik.data.db.full.EventFull { *; }
-keep class pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel { *; }
-keepclassmembers class pl.szczodrzynski.edziennik.widgets.WidgetConfig { public *; }
-keepnames class pl.szczodrzynski.edziennik.WidgetTimetable
-keepnames class pl.szczodrzynski.edziennik.notifications.WidgetNotifications
-keepnames class pl.szczodrzynski.edziennik.luckynumber.WidgetLuckyNumber
-keepclassmembers class pl.szczodrzynski.edziennik.ui.widgets.WidgetConfig { public *; }
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.timetable.WidgetTimetableProvider
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.notifications.WidgetNotificationsProvider
-keepnames class pl.szczodrzynski.edziennik.widgets.luckynumber.WidgetLuckyNumber
-keep class .R
-keep class **.R$* {

View File

@ -3,6 +3,17 @@
xmlns:tools="http://schemas.android.com/tools"
package="pl.szczodrzynski.edziennik">
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:name=".App"
android:allowBackup="true"
@ -15,12 +26,16 @@
android:theme="@style/AppTheme.Dark"
android:usesCleartextTraffic="true"
tools:ignore="UnusedAttribute">
<activity
android:name=".ui.modules.login.LoginLibrusCaptchaActivity"
android:theme="@android:style/Theme.Dialog"
android:excludeFromRecents="true"/>
<activity
android:name=".MainActivity"
<!-- __ __ _ _ _ _ _
| \/ | (_) /\ | | (_) (_) |
| \ / | __ _ _ _ __ / \ ___| |_ ___ ___| |_ _ _
| |\/| |/ _` | | '_ \ / /\ \ / __| __| \ \ / / | __| | | |
| | | | (_| | | | | | / ____ \ (__| |_| |\ V /| | |_| |_| |
|_| |_|\__,_|_|_| |_| /_/ \_\___|\__|_| \_/ |_|\__|\__, |
__/ |
|___/ -->
<activity android:name=".MainActivity"
android:configChanges="orientation|screenSize"
android:label="@string/app_name"
android:launchMode="singleTop"
@ -32,58 +47,7 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ui.modules.feedback.FeedbackActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:label="@string/app_name"
android:theme="@style/AppTheme" />
<activity
android:name=".ui.modules.login.LoginActivity"
android:configChanges="orientation|screenSize"
android:launchMode="singleTop"
android:theme="@style/AppTheme.Light" />
<activity
android:name=".ui.modules.intro.ChangelogIntroActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name"
android:theme="@style/Theme.Intro" />
<!--
______ _ _
| ____(_) | |
| |__ _ _ __ ___| |__ __ _ ___ ___
| __| | | '__/ _ \ '_ \ / _` / __|/ _ \
| | | | | | __/ |_) | (_| \__ \ __/
|_| |_|_| \___|_.__/ \__,_|___/\___/
-->
<activity
android:name=".ui.modules.base.CrashActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:process=":error_activity"
android:theme="@style/DeadTheme" />
<!--
_____ _ _ _ _ _
/ ____| | | | | (_) (_) |
| | _ __ __ _ ___| |__ __ _ ___| |_ ___ ___| |_ _ _
| | | '__/ _` / __| '_ \ / _` |/ __| __| \ \ / / | __| | | |
| |____| | | (_| \__ \ | | | | (_| | (__| |_| |\ V /| | |_| |_| |
\_____|_| \__,_|___/_| |_| \__,_|\___|\__|_| \_/ |_|\__|\__, |
__/ |
|___/
-->
<activity
android:name=".ui.modules.base.CrashGtfoActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:theme="@style/DeadTheme" />
<activity
android:name=".widgets.WidgetConfigActivity"
android:configChanges="orientation|keyboardHidden"
android:excludeFromRecents="true"
android:noHistory="true"
android:theme="@style/AppTheme.NoDisplay">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
<!--
__ ___ _ _
\ \ / (_) | | | |
@ -92,68 +56,36 @@
\ /\ / | | (_| | (_| | __/ |_ \__ \
\/ \/ |_|\__,_|\__, |\___|\__||___/
__/ |
|_
|___/
-->
<activity android:name=".widgets.timetable.LessonDialogActivity"
<activity android:name=".ui.widgets.WidgetConfigActivity"
android:configChanges="orientation|keyboardHidden"
android:excludeFromRecents="true"
android:noHistory="true"
android:theme="@style/AppTheme.NoDisplay" />
<activity
android:name=".ui.modules.settings.SettingsLicenseActivity"
android:configChanges="orientation|keyboardHidden"
android:theme="@style/AppTheme" />
<activity
android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
android:configChanges="orientation|keyboardHidden"
android:theme="@style/Base.Theme.AppCompat" />
<activity
android:name=".ui.modules.webpush.WebPushConfigActivity"
android:configChanges="orientation|keyboardHidden"
android:theme="@style/AppTheme.Dark" />
<activity
android:name=".ui.modules.home.CounterActivity"
android:theme="@style/AppTheme.Black" />
<activity android:name=".ui.modules.webpush.QrScannerActivity" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
<!--
_____ _ _
| __ \ (_) | |
| |__) | __ _____ ___ __| | ___ _ __ ___
| ___/ '__/ _ \ \ / / |/ _` |/ _ \ '__/ __|
| | | | | (_) \ V /| | (_| | __/ | \__ \
|_| |_| \___/ \_/ |_|\__,_|\___|_| |___/
-->
<receiver
android:name=".WidgetTimetable"
android:theme="@style/AppTheme.NoDisplay">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
<!-- TIMETABLE -->
<receiver android:name=".ui.widgets.timetable.WidgetTimetableProvider"
android:label="@string/widget_timetable_title">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_timetable_info" />
</receiver>
<!--
____ _ _
| _ \ | | (_)
| |_) | ___ ___ | |_ _ __ ___ ___ ___ ___ _____ _ __
| _ < / _ \ / _ \| __| | '__/ _ \/ __/ _ \ \ \ / / _ \ '__|
| |_) | (_) | (_) | |_ | | | __/ (_| __/ |\ V / __/ |
|____/ \___/ \___/ \__| |_| \___|\___\___|_| \_/ \_____|
-->
<receiver
android:name=".widgets.notifications.WidgetNotifications"
<service android:name=".ui.widgets.timetable.WidgetTimetableService"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<activity android:name=".ui.widgets.LessonDialogActivity"
android:configChanges="orientation|keyboardHidden"
android:excludeFromRecents="true"
android:noHistory="true"
android:theme="@style/AppTheme.NoDisplay" />
<!-- NOTIFICATIONS -->
<receiver android:name=".ui.widgets.notifications.WidgetNotificationsProvider"
android:label="@string/widget_notifications_title">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
@ -163,8 +95,10 @@
android:name="android.appwidget.provider"
android:resource="@xml/widget_notifications_info" />
</receiver>
<receiver
android:name=".widgets.luckynumber.WidgetLuckyNumber"
<service android:name=".ui.widgets.notifications.WidgetNotificationsService"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<!-- LUCKY NUMBER -->
<receiver android:name=".widgets.luckynumber.WidgetLuckyNumber"
android:label="@string/widget_lucky_number_title">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
@ -174,8 +108,58 @@
android:name="android.appwidget.provider"
android:resource="@xml/widget_lucky_number_info" />
</receiver>
<receiver
android:name=".receivers.UserPresentReceiver"
<!-- _ _ _ _ _
/\ | | (_) (_) | (_)
/ \ ___| |_ ___ ___| |_ _ ___ ___
/ /\ \ / __| __| \ \ / / | __| |/ _ \/ __|
/ ____ \ (__| |_| |\ V /| | |_| | __/\__ \
/_/ \_\___|\__|_| \_/ |_|\__|_|\___||___/
-->
<activity android:name=".ui.modules.base.CrashActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:process=":error_activity"
android:theme="@style/DeadTheme" />
<activity android:name=".ui.modules.base.CrashGtfoActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:theme="@style/DeadTheme" />
<activity android:name=".ui.modules.intro.ChangelogIntroActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name"
android:theme="@style/Theme.Intro" />
<activity android:name=".ui.modules.login.LoginActivity"
android:configChanges="orientation|screenSize"
android:launchMode="singleTop"
android:theme="@style/AppTheme.Light" />
<activity android:name=".ui.modules.login.LoginLibrusCaptchaActivity"
android:theme="@android:style/Theme.Dialog"
android:excludeFromRecents="true"/>
<activity android:name=".ui.modules.home.CounterActivity"
android:theme="@style/AppTheme.Black" />
<activity android:name=".ui.modules.feedback.FeedbackActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:label="@string/app_name"
android:theme="@style/AppTheme" />
<activity android:name=".ui.modules.settings.SettingsLicenseActivity"
android:configChanges="orientation|keyboardHidden"
android:theme="@style/AppTheme" />
<activity android:name=".ui.modules.webpush.WebPushConfigActivity"
android:configChanges="orientation|keyboardHidden"
android:theme="@style/AppTheme.Dark" />
<activity android:name=".ui.modules.webpush.QrScannerActivity" />
<activity android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
android:configChanges="orientation|keyboardHidden"
android:theme="@style/Base.Theme.AppCompat" />
<!-- _____ _
| __ \ (_)
| |__) |___ ___ ___ ___ _____ _ __ ___
| _ // _ \/ __/ _ \ \ \ / / _ \ '__/ __|
| | \ \ __/ (_| __/ |\ V / __/ | \__ \
|_| \_\___|\___\___|_| \_/ \___|_| |___/
-->
<receiver android:name=".receivers.UserPresentReceiver"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.USER_PRESENT" />
@ -188,54 +172,53 @@
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
<receiver
android:name=".sync.FirebaseBroadcastReceiver"
<receiver android:name=".sync.FirebaseBroadcastReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</receiver>
<receiver
android:name=".receivers.SzkolnyReceiver"
<receiver android:name=".receivers.SzkolnyReceiver"
android:exported="true">
<intent-filter>
<action android:name="pl.szczodrzynski.edziennik.SZKOLNY_MAIN" />
</intent-filter>
</receiver>
<service
android:name=".sync.MyFirebaseMessagingService"
<!-- _____ _
/ ____| (_)
| (___ ___ _ ____ ___ ___ ___ ___
\___ \ / _ \ '__\ \ / / |/ __/ _ \/ __|
____) | __/ | \ V /| | (_| __/\__ \
|_____/ \___|_| \_/ |_|\___\___||___/
-->
<service android:name=".sync.MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service
android:name=".widgets.timetable.WidgetTimetableService"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<service
android:name=".widgets.notifications.WidgetNotificationsService"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<service android:name=".receivers.BootReceiver$NotificationActionService" />
<service android:name=".Notifier$GetDataRetryService" />
<service android:name=".data.api.ApiService" />
<service android:name="pl.szczodrzynski.edziennik.data.api.ApiService" />
<!--
_____ _ _
| __ \ (_) | |
| |__) | __ _____ ___ __| | ___ _ __ ___
| ___/ '__/ _ \ \ / / |/ _` |/ _ \ '__/ __|
| | | | | (_) \ V /| | (_| | __/ | \__ \
|_| |_| \___/ \_/ |_|\__,_|\___|_| |___/
-->
<provider android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
</application>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
</manifest>

View File

@ -6,6 +6,7 @@ import android.content.Context
import android.content.pm.PackageManager
import android.content.res.ColorStateList
import android.content.res.Resources
import android.database.Cursor
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.Typeface
@ -26,6 +27,9 @@ import android.widget.CompoundButton
import android.widget.TextView
import androidx.annotation.*
import androidx.core.app.ActivityCompat
import androidx.core.database.getIntOrNull
import androidx.core.database.getLongOrNull
import androidx.core.database.getStringOrNull
import androidx.core.util.forEach
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
@ -938,3 +942,7 @@ fun Context.getNotificationTitle(type: Int): String {
else -> R.string.notification_type_general
})
}
fun Cursor?.getString(columnName: String) = this?.getStringOrNull(getColumnIndex(columnName))
fun Cursor?.getInt(columnName: String) = this?.getIntOrNull(getColumnIndex(columnName))
fun Cursor?.getLong(columnName: String) = this?.getLongOrNull(getColumnIndex(columnName))

View File

@ -4,6 +4,7 @@
package pl.szczodrzynski.edziennik.config
import com.google.gson.JsonObject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@ -74,6 +75,11 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
get() { mRunSync = mRunSync ?: values.get("runSync", false); return mRunSync ?: false }
set(value) { set("runSync", value); mRunSync = value }
private var mWidgetConfigs: JsonObject? = null
var widgetConfigs: JsonObject
get() { mWidgetConfigs = mWidgetConfigs ?: values.get("widgetConfigs", JsonObject()); return mWidgetConfigs ?: JsonObject() }
set(value) { set("widgetConfigs", value); mWidgetConfigs = value }
private var rawEntries: List<ConfigEntry> = db.configDao().getAllNow()
private val profileConfigs: HashMap<Int, ProfileConfig> = hashMapOf()
init {

View File

@ -10,38 +10,14 @@ import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.interfaces.EndpointCallback
import pl.szczodrzynski.edziennik.data.db.AppDb
import pl.szczodrzynski.edziennik.data.db.entity.Announcement
import pl.szczodrzynski.edziennik.data.db.entity.EndpointTimer
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
import pl.szczodrzynski.edziennik.data.db.entity.AttendanceType
import pl.szczodrzynski.edziennik.data.db.entity.Classroom
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.EventType
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.LessonRange
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Notice
import pl.szczodrzynski.edziennik.data.db.entity.NoticeType
import pl.szczodrzynski.edziennik.data.db.entity.Notification
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Subject
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.entity.TeacherAbsence
import pl.szczodrzynski.edziennik.data.db.entity.TeacherAbsenceType
import pl.szczodrzynski.edziennik.data.db.entity.Team
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
import pl.szczodrzynski.edziennik.data.db.entity.LibrusLesson
import pl.szczodrzynski.edziennik.data.db.entity.*
import pl.szczodrzynski.edziennik.singleOrNull
import pl.szczodrzynski.edziennik.toSparseArray
import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.values
import java.io.InterruptedIOException
import java.net.ConnectException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import javax.net.ssl.SSLException
@ -390,7 +366,7 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
apiError.errorCode = when (apiError.throwable) {
is UnknownHostException -> ERROR_REQUEST_FAILURE_HOSTNAME_NOT_FOUND
is SSLException -> ERROR_REQUEST_FAILURE_SSL_ERROR
is InterruptedIOException -> ERROR_REQUEST_FAILURE_NO_INTERNET
is InterruptedIOException, is ConnectException -> ERROR_REQUEST_FAILURE_NO_INTERNET
is SocketTimeoutException -> ERROR_REQUEST_FAILURE_TIMEOUT
else ->
if (apiError.errorCode == ERROR_REQUEST_FAILURE)

View File

@ -4,6 +4,7 @@
package pl.szczodrzynski.edziennik.data.db.dao
import android.database.Cursor
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
@ -36,4 +37,7 @@ interface NotificationDao {
@Query("UPDATE notifications SET posted = 1 WHERE posted = 0")
fun setAllPosted()
@Query("SELECT * FROM notifications ORDER BY addedDate DESC")
fun getAllCursor(): Cursor
}

View File

@ -13,6 +13,10 @@ import pl.szczodrzynski.edziennik.data.api.events.requests.TaskCancelRequest
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask
class SzkolnyReceiver : BroadcastReceiver() {
companion object {
const val ACTION = "pl.szczodrzynski.edziennik.SZKOLNY_MAIN"
}
override fun onReceive(context: Context?, intent: Intent?) {
context ?: return
when (intent?.extras?.getString("task", null)) {

View File

@ -6,7 +6,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import pl.szczodrzynski.edziennik.WidgetTimetable;
import pl.szczodrzynski.edziennik.ui.widgets.timetable.WidgetTimetableProvider;
public class UserPresentReceiver extends BroadcastReceiver {
@Override
@ -14,12 +14,12 @@ public class UserPresentReceiver extends BroadcastReceiver {
if (intent.getAction() != null) {
if (intent.getAction().equals(Intent.ACTION_USER_PRESENT)) {
//Toast.makeText(context, "User is present", Toast.LENGTH_SHORT).show();
Intent widgetIntent = new Intent(context, WidgetTimetable.class);
Intent widgetIntent = new Intent(context, WidgetTimetableProvider.class);
widgetIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
// Use an array and EXTRA_APPWIDGET_IDS instead of AppWidgetManager.EXTRA_APPWIDGET_ID,
// since it seems the onUpdate() is only fired on that:
int[] ids = AppWidgetManager.getInstance(context)
.getAppWidgetIds(new ComponentName(context, WidgetTimetable.class));
.getAppWidgetIds(new ComponentName(context, WidgetTimetableProvider.class));
widgetIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids);
context.sendBroadcast(widgetIntent);
}

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2019-11-14.
*/
package pl.szczodrzynski.edziennik.widgets.timetable
package pl.szczodrzynski.edziennik.ui.widgets
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT

View File

@ -1,4 +1,8 @@
package pl.szczodrzynski.edziennik.widgets;
/*
* Copyright (c) Kuba Szczodrzyński 2020-1-6.
*/
package pl.szczodrzynski.edziennik.ui.widgets;
public class WidgetConfig {
public int profileId = -1;

View File

@ -1,4 +1,8 @@
package pl.szczodrzynski.edziennik.widgets;
/*
* Copyright (c) Kuba Szczodrzyński 2020-1-6.
*/
package pl.szczodrzynski.edziennik.ui.widgets;
import android.app.Activity;
import android.app.WallpaperManager;
@ -15,16 +19,17 @@ import android.widget.SeekBar;
import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.simplelist.MaterialSimpleListAdapter;
import com.afollestad.materialdialogs.simplelist.MaterialSimpleListItem;
import com.google.gson.JsonObject;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.WidgetTimetable;
import pl.szczodrzynski.edziennik.data.db.entity.Profile;
import pl.szczodrzynski.edziennik.databinding.DialogWidgetConfigBinding;
import pl.szczodrzynski.edziennik.ui.widgets.notifications.WidgetNotificationsProvider;
import pl.szczodrzynski.edziennik.ui.widgets.timetable.WidgetTimetableProvider;
import pl.szczodrzynski.edziennik.widgets.luckynumber.WidgetLuckyNumber;
import pl.szczodrzynski.edziennik.widgets.notifications.WidgetNotifications;
import static pl.szczodrzynski.edziennik.ExtensionsKt.filterOutArchived;
@ -130,17 +135,19 @@ public class WidgetConfigActivity extends Activity {
.positiveText(R.string.ok)
.negativeText(R.string.cancel)
.onPositive(((dialog1, which) -> {
app.appConfig.widgetTimetableConfigs.put(mAppWidgetId, new WidgetConfig(profileId, bigStyle, darkTheme, opacity));
app.saveConfig("widgetTimetableConfigs");
WidgetConfig config = new WidgetConfig(profileId, bigStyle, darkTheme, opacity);
JsonObject configs = app.config.getWidgetConfigs();
configs.add(Integer.toString(mAppWidgetId), app.gson.toJsonTree(config));
app.config.setWidgetConfigs(configs);
Intent refreshIntent;
switch (widgetType) {
default:
case WIDGET_TIMETABLE:
refreshIntent = new Intent(app.getContext(), WidgetTimetable.class);
refreshIntent = new Intent(app.getContext(), WidgetTimetableProvider.class);
break;
case WIDGET_NOTIFICATIONS:
refreshIntent = new Intent(app.getContext(), WidgetNotifications.class);
refreshIntent = new Intent(app.getContext(), WidgetNotificationsProvider.class);
break;
case WIDGET_LUCKY_NUMBER:
refreshIntent = new Intent(app.getContext(), WidgetLuckyNumber.class);

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-1-6.
*/
package pl.szczodrzynski.edziennik.ui.widgets.notifications
import android.content.Intent
import android.database.Cursor
import android.os.Binder
import android.widget.AdapterView
import android.widget.RemoteViews
import android.widget.RemoteViewsService
import com.google.gson.JsonParser
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.entity.Notification
import pl.szczodrzynski.edziennik.ui.widgets.WidgetConfig
import pl.szczodrzynski.edziennik.utils.models.Date
class WidgetNotificationsFactory(val app: App, val config: WidgetConfig) : RemoteViewsService.RemoteViewsFactory {
companion object {
private const val TAG = "WidgetNotificationsFactory"
}
private var cursor: Cursor? = null
override fun onDataSetChanged() {
cursor?.close()
Binder.clearCallingIdentity().let {
cursor = app.db.notificationDao().getAllCursor()
Binder.restoreCallingIdentity(it)
}
}
override fun getViewAt(position: Int): RemoteViews? {
if (position == AdapterView.INVALID_POSITION || cursor?.moveToPosition(position) != true)
return null
val views: RemoteViews = if (config.bigStyle) {
RemoteViews(app.packageName, if (config.darkTheme) R.layout.row_widget_notifications_dark_big_item else R.layout.row_widget_notifications_big_item)
} else {
RemoteViews(app.packageName, if (config.darkTheme) R.layout.row_widget_notifications_dark_item else R.layout.row_widget_notifications_item)
}
val notification = cursor?.run {
Notification(
getInt("id") ?: 0,
getString("title") ?: "",
getString("text") ?: "",
getInt("type") ?: 0,
getInt("profileId"),
getString("profileName"),
getInt("posted") == 1,
getInt("viewId"),
getString("extras")?.let { JsonParser().parse(it).asJsonObject },
getLong("addedDate") ?: System.currentTimeMillis()
)
} ?: return views
views.apply {
setTextViewText(R.id.widgetNotificationsTitle,
app.getString(R.string.widget_notifications_title_format, notification.title, app.getNotificationTitle(notification.type)))
setTextViewText(R.id.widgetNotificationsText, notification.text)
setTextViewText(R.id.widgetNotificationsDate, Date.fromMillis(notification.addedDate).formattedString)
setOnClickFillInIntent(R.id.widgetNotificationsRoot, Intent().also { notification.fillIntent(it) })
}
return views
}
override fun getItemId(position: Int): Long = if (cursor?.moveToPosition(position) == true)
cursor?.getLong("id") ?: position.toLong() else position.toLong()
override fun getCount() = cursor?.count ?: 0
override fun onCreate() {}
override fun getLoadingView() = null
override fun hasStableIds() = true
override fun getViewTypeCount() = 1
override fun onDestroy() = cursor?.close() ?: Unit
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-1-6.
*/
package pl.szczodrzynski.edziennik.ui.widgets.notifications
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.net.Uri
import android.view.View
import android.widget.RemoteViews
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.receivers.SzkolnyReceiver
import pl.szczodrzynski.edziennik.ui.widgets.WidgetConfig
class WidgetNotificationsProvider : AppWidgetProvider() {
companion object {
private const val TAG = "WidgetNotificationsProvider"
}
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
val app = context.applicationContext as App
val widgetConfigs = app.config.widgetConfigs
for (appWidgetId in appWidgetIds) {
val config = widgetConfigs.getJsonObject(appWidgetId.toString())?.let { app.gson.fromJson(it, WidgetConfig::class.java) } ?: continue
val iconSize = if (config.bigStyle) 24 else 16
val views: RemoteViews = if (config.bigStyle) {
RemoteViews(app.packageName, if (config.darkTheme) R.layout.widget_notifications_dark_big else R.layout.widget_notifications_big)
} else {
RemoteViews(app.packageName, if (config.darkTheme) R.layout.widget_notifications_dark else R.layout.widget_notifications)
}
val syncIntent = Intent(SzkolnyReceiver.ACTION)
syncIntent.putExtra("task", "SyncRequest")
val syncPendingIntent = PendingIntent.getBroadcast(context, 0, syncIntent, 0)
views.setOnClickPendingIntent(R.id.widgetNotificationsSync, syncPendingIntent)
views.setImageViewBitmap(
R.id.widgetNotificationsSync,
IconicsDrawable(context, CommunityMaterial.Icon.cmd_download_outline)
.colorInt(Color.WHITE)
.sizeDp(iconSize)
.toBitmap()
)
views.setViewVisibility(R.id.widgetNotificationsLoading, View.GONE)
val listIntent = Intent(context, WidgetNotificationsService::class.java)
listIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
listIntent.putExtra("config", app.gson.toJson(config))
listIntent.data = Uri.parse(listIntent.toUri(Intent.URI_INTENT_SCHEME))
views.setRemoteAdapter(R.id.widgetNotificationsListView, listIntent)
val itemIntent = Intent(context, MainActivity::class.java)
itemIntent.action = Intent.ACTION_MAIN
val itemPendingIntent = PendingIntent.getActivity(context, 0, itemIntent, 0)
views.setPendingIntentTemplate(R.id.widgetNotificationsListView, itemPendingIntent)
val headerIntent = Intent(context, MainActivity::class.java)
headerIntent.action = Intent.ACTION_MAIN
headerIntent.putExtra("fragmentId", MainActivity.DRAWER_ITEM_NOTIFICATIONS)
val headerPendingIntent = PendingIntent.getActivity(context, 0, headerIntent, 0)
views.setOnClickPendingIntent(R.id.widgetNotificationsHeader, headerPendingIntent)
appWidgetManager.updateAppWidget(appWidgetId, views)
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widgetNotificationsListView)
}
}
override fun onDeleted(context: Context, appWidgetIds: IntArray) {
val app = context.applicationContext as App
val widgetConfigs = app.config.widgetConfigs
appWidgetIds.forEach {
widgetConfigs.remove(it.toString())
}
app.config.widgetConfigs = widgetConfigs
}
}

View File

@ -0,0 +1,18 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-1-6.
*/
package pl.szczodrzynski.edziennik.ui.widgets.notifications
import android.content.Intent
import android.widget.RemoteViewsService
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.ui.widgets.WidgetConfig
class WidgetNotificationsService : RemoteViewsService() {
override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
val app = application as App
val config = intent.getStringExtra("config")?.let { app.gson.fromJson(it, WidgetConfig::class.java) }
return WidgetNotificationsFactory(app, config ?: WidgetConfig(-1))
}
}

View File

@ -1,4 +1,8 @@
package pl.szczodrzynski.edziennik.widgets.timetable;
/*
* Copyright (c) Kuba Szczodrzyński 2020-1-6.
*/
package pl.szczodrzynski.edziennik.ui.widgets.timetable;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
@ -28,14 +32,13 @@ import com.mikepenz.iconics.typeface.library.community.material.CommunityMateria
import java.util.List;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.WidgetTimetable;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.models.ItemWidgetTimetableModel;
import pl.szczodrzynski.edziennik.utils.models.Time;
import static android.util.TypedValue.COMPLEX_UNIT_SP;
public class WidgetTimetableListProvider implements RemoteViewsService.RemoteViewsFactory {
public class WidgetTimetableFactory implements RemoteViewsService.RemoteViewsFactory {
private static final String TAG = "WidgetTimetableProvider";
private Context context;
@ -44,7 +47,7 @@ public class WidgetTimetableListProvider implements RemoteViewsService.RemoteVie
private static boolean triedToReload = false;
//For obtaining the activity's context and intent
public WidgetTimetableListProvider(Context context, Intent intent) {
public WidgetTimetableFactory(Context context, Intent intent) {
this.context = context;
this.appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0);
// executed only ONCE
@ -65,7 +68,7 @@ public class WidgetTimetableListProvider implements RemoteViewsService.RemoteVie
public void onDataSetChanged() {
// executed EVERY TIME
Log.d(TAG, "onDataSetChanged for appWidgetId: "+appWidgetId);
lessons = WidgetTimetable.Companion.getTimetables() == null ? null : WidgetTimetable.Companion.getTimetables().get(appWidgetId);
lessons = WidgetTimetableProvider.Companion.getTimetables() == null ? null : WidgetTimetableProvider.Companion.getTimetables().get(appWidgetId);
}
@Override
@ -306,11 +309,11 @@ public class WidgetTimetableListProvider implements RemoteViewsService.RemoteVie
// try to reload the widget
// only once
triedToReload = true;
Intent intent = new Intent(context, WidgetTimetable.class);
Intent intent = new Intent(context, WidgetTimetableProvider.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
// Use an array and EXTRA_APPWIDGET_IDS instead of AppWidgetManager.EXTRA_APPWIDGET_ID,
// since it seems the onUpdate() is only fired on that:
int[] ids = AppWidgetManager.getInstance(context).getAppWidgetIds(new ComponentName(context, WidgetTimetable.class));
int[] ids = AppWidgetManager.getInstance(context).getAppWidgetIds(new ComponentName(context, WidgetTimetableProvider.class));
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids);
context.sendBroadcast(intent);
}

View File

@ -1,4 +1,8 @@
package pl.szczodrzynski.edziennik
/*
* Copyright (c) Kuba Szczodrzyński 2020-1-6.
*/
package pl.szczodrzynski.edziennik.ui.widgets.timetable
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
@ -21,20 +25,50 @@ import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask
import pl.szczodrzynski.edziennik.data.db.entity.Event.TYPE_HOMEWORK
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
import pl.szczodrzynski.edziennik.ui.widgets.LessonDialogActivity
import pl.szczodrzynski.edziennik.ui.widgets.WidgetConfig
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.ItemWidgetTimetableModel
import pl.szczodrzynski.edziennik.utils.models.Time
import pl.szczodrzynski.edziennik.utils.models.Week
import pl.szczodrzynski.edziennik.widgets.WidgetConfig
import pl.szczodrzynski.edziennik.widgets.timetable.LessonDialogActivity
import pl.szczodrzynski.edziennik.widgets.timetable.WidgetTimetableService
import java.lang.reflect.InvocationTargetException
class WidgetTimetable : AppWidgetProvider() {
class WidgetTimetableProvider : AppWidgetProvider() {
companion object {
const val ACTION_SYNC_DATA = "ACTION_SYNC_DATA"
private const val TAG = "WidgetTimetable"
var timetables: SparseArray<List<ItemWidgetTimetableModel>>? = null
fun getPendingSelfIntent(context: Context, action: String): PendingIntent {
val intent = Intent(context, WidgetTimetableProvider::class.java)
intent.action = action
return getPendingSelfIntent(context, intent)
}
fun getPendingSelfIntent(context: Context, intent: Intent): PendingIntent {
return PendingIntent.getBroadcast(context, 0, intent, 0)
}
fun drawableToBitmap(drawable: Drawable): Bitmap {
if (drawable is BitmapDrawable) {
return drawable.bitmap
}
val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
return bitmap
}
}
override fun onReceive(context: Context, intent: Intent) {
if (ACTION_SYNC_DATA == intent.action) {
@ -46,12 +80,12 @@ class WidgetTimetable : AppWidgetProvider() {
private val ignoreCancelled = true
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
val thisWidget = ComponentName(context, WidgetTimetable::class.java)
val thisWidget = ComponentName(context, WidgetTimetableProvider::class.java)
timetables = SparseArray()
//timetables.clear();
val app = context.applicationContext as App
val widgetConfigs = app.config.widgetConfigs
var bellSyncDiffMillis: Long = 0
app.config.timetable.bellSyncDiff?.let {
@ -63,37 +97,32 @@ class WidgetTimetable : AppWidgetProvider() {
val allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget)
allWidgetIds?.forEach { appWidgetId ->
var widgetConfig = app.appConfig.widgetTimetableConfigs[appWidgetId]
if (widgetConfig == null) {
widgetConfig = WidgetConfig(app.profileFirstId())
app.appConfig.widgetTimetableConfigs[appWidgetId] = widgetConfig
app.appConfig.savePending = true
}
val config = widgetConfigs.getJsonObject(appWidgetId.toString())?.let { app.gson.fromJson(it, WidgetConfig::class.java) } ?: return@forEach
val views = if (widgetConfig.bigStyle) {
RemoteViews(context.packageName, if (widgetConfig.darkTheme) R.layout.widget_timetable_dark_big else R.layout.widget_timetable_big)
val views = if (config.bigStyle) {
RemoteViews(context.packageName, if (config.darkTheme) R.layout.widget_timetable_dark_big else R.layout.widget_timetable_big)
} else {
RemoteViews(context.packageName, if (widgetConfig.darkTheme) R.layout.widget_timetable_dark else R.layout.widget_timetable)
RemoteViews(context.packageName, if (config.darkTheme) R.layout.widget_timetable_dark else R.layout.widget_timetable)
}
val refreshIntent = Intent(app, WidgetTimetable::class.java)
val refreshIntent = Intent(app, WidgetTimetableProvider::class.java)
refreshIntent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
refreshIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds)
val pendingRefreshIntent = PendingIntent.getBroadcast(context,
val refreshPendingIntent = PendingIntent.getBroadcast(context,
0, refreshIntent, PendingIntent.FLAG_UPDATE_CURRENT)
views.setOnClickPendingIntent(R.id.widgetTimetableRefresh, pendingRefreshIntent)
views.setOnClickPendingIntent(R.id.widgetTimetableRefresh, refreshPendingIntent)
views.setOnClickPendingIntent(R.id.widgetTimetableSync, getPendingSelfIntent(context, ACTION_SYNC_DATA))
views.setImageViewBitmap(R.id.widgetTimetableRefresh, IconicsDrawable(context, CommunityMaterial.Icon2.cmd_refresh)
.colorInt(Color.WHITE)
.sizeDp(if (widgetConfig.bigStyle) 24 else 16).toBitmap())
.sizeDp(if (config.bigStyle) 24 else 16).toBitmap())
views.setImageViewBitmap(R.id.widgetTimetableSync, IconicsDrawable(context, CommunityMaterial.Icon.cmd_download_outline)
.colorInt(Color.WHITE)
.sizeDp(if (widgetConfig.bigStyle) 24 else 16).toBitmap())
.sizeDp(if (config.bigStyle) 24 else 16).toBitmap())
prepareAppWidget(app, appWidgetId, views, widgetConfig, bellSyncDiffMillis)
prepareAppWidget(app, appWidgetId, views, config, bellSyncDiffMillis)
appWidgetManager.updateAppWidget(appWidgetId, views)
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widgetTimetableListView)
@ -329,20 +358,20 @@ class WidgetTimetable : AppWidgetProvider() {
}
// intent running when the header is clicked
val openIntent = Intent(app, MainActivity::class.java)
openIntent.action = "android.intent.action.MAIN"
val headerIntent = Intent(app, MainActivity::class.java)
headerIntent.action = "android.intent.action.MAIN"
if (!unified) {
// per-profile widget should redirect to it + correct day
profileId?.let {
openIntent.putExtra("profileId", it)
headerIntent.putExtra("profileId", it)
}
displayingDate?.let {
openIntent.putExtra("timetableDate", it.value)
headerIntent.putExtra("timetableDate", it.value)
}
}
openIntent.putExtra("fragmentId", MainActivity.DRAWER_ITEM_TIMETABLE)
val pendingOpenIntent = PendingIntent.getActivity(app, appWidgetId, openIntent, 0)
views.setOnClickPendingIntent(R.id.widgetTimetableHeader, pendingOpenIntent)
headerIntent.putExtra("fragmentId", MainActivity.DRAWER_ITEM_TIMETABLE)
val headerPendingIntent = PendingIntent.getActivity(app, appWidgetId, headerIntent, 0)
views.setOnClickPendingIntent(R.id.widgetTimetableHeader, headerPendingIntent)
timetables!!.put(appWidgetId, models)
@ -353,55 +382,21 @@ class WidgetTimetable : AppWidgetProvider() {
views.setRemoteAdapter(R.id.widgetTimetableListView, listIntent)
// create an intent used to display the lesson details dialog
val intentTemplate = Intent(app, LessonDialogActivity::class.java)
intentTemplate.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK /*or Intent.FLAG_ACTIVITY_CLEAR_TASK*/)
val pendingIntentTimetable = PendingIntent.getActivity(app, appWidgetId, intentTemplate, 0)
views.setPendingIntentTemplate(R.id.widgetTimetableListView, pendingIntentTimetable)
val itemIntent = Intent(app, LessonDialogActivity::class.java)
itemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK /*or Intent.FLAG_ACTIVITY_CLEAR_TASK*/)
val itemPendingIntent = PendingIntent.getActivity(app, appWidgetId, itemIntent, 0)
views.setPendingIntentTemplate(R.id.widgetTimetableListView, itemPendingIntent)
if (!unified)
views.setScrollPosition(R.id.widgetTimetableListView, scrollPos)
}
override fun onEnabled(context: Context) {
// Enter relevant functionality for when the first widget is created
}
override fun onDeleted(context: Context, appWidgetIds: IntArray) {
val app = context.applicationContext as App
for (appWidgetId in appWidgetIds) {
app.appConfig.widgetTimetableConfigs.remove(appWidgetId)
}
app.saveConfig("widgetTimetableConfigs")
}
companion object {
const val ACTION_SYNC_DATA = "ACTION_SYNC_DATA"
private const val TAG = "WidgetTimetable"
var timetables: SparseArray<List<ItemWidgetTimetableModel>>? = null
fun getPendingSelfIntent(context: Context, action: String): PendingIntent {
val intent = Intent(context, WidgetTimetable::class.java)
intent.action = action
return getPendingSelfIntent(context, intent)
}
fun getPendingSelfIntent(context: Context, intent: Intent): PendingIntent {
return PendingIntent.getBroadcast(context, 0, intent, 0)
}
fun drawableToBitmap(drawable: Drawable): Bitmap {
if (drawable is BitmapDrawable) {
return drawable.bitmap
}
val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
return bitmap
val widgetConfigs = app.config.widgetConfigs
appWidgetIds.forEach {
widgetConfigs.remove(it.toString())
}
app.config.widgetConfigs = widgetConfigs
}
}

View File

@ -0,0 +1,15 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-1-6.
*/
package pl.szczodrzynski.edziennik.ui.widgets.timetable;
import android.content.Intent;
import android.widget.RemoteViewsService;
public class WidgetTimetableService extends RemoteViewsService {
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return (new WidgetTimetableFactory(this.getApplicationContext(), intent));
}
}

View File

@ -8,7 +8,7 @@ import java.util.Map;
import java.util.TreeMap;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.widgets.WidgetConfig;
import pl.szczodrzynski.edziennik.ui.widgets.WidgetConfig;
public class AppConfig {

View File

@ -26,7 +26,7 @@ import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask;
import pl.szczodrzynski.edziennik.data.db.entity.Profile;
import pl.szczodrzynski.edziennik.widgets.WidgetConfig;
import pl.szczodrzynski.edziennik.ui.widgets.WidgetConfig;
import static pl.szczodrzynski.edziennik.utils.Utils.getCellsForSize;

View File

@ -1,171 +0,0 @@
package pl.szczodrzynski.edziennik.widgets.notifications;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Build;
import android.view.View;
import android.widget.RemoteViews;
import com.mikepenz.iconics.IconicsColor;
import com.mikepenz.iconics.IconicsDrawable;
import com.mikepenz.iconics.IconicsSize;
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask;
import pl.szczodrzynski.edziennik.widgets.WidgetConfig;
public class WidgetNotifications extends AppWidgetProvider {
public static final String ACTION_SYNC_DATA = "ACTION_SYNC_DATA";
private static final String TAG = "WidgetNotifications";
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_SYNC_DATA.equals(intent.getAction())){
EdziennikTask.Companion.sync().enqueue(context);
}
super.onReceive(context, intent);
}
public static PendingIntent getPendingSelfIntent(Context context, String action) {
Intent intent = new Intent(context, WidgetNotifications.class);
intent.setAction(action);
return getPendingSelfIntent(context, intent);
}
public static PendingIntent getPendingSelfIntent(Context context, Intent intent) {
return PendingIntent.getBroadcast(context, 0, intent, 0);
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
ComponentName thisWidget = new ComponentName(context, WidgetNotifications.class);
App app = (App)context.getApplicationContext();
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
// There may be multiple widgets active, so update all of them
for (int appWidgetId : allWidgetIds) {
WidgetConfig widgetConfig = app.appConfig.widgetTimetableConfigs.get(appWidgetId);
if (widgetConfig == null) {
widgetConfig = new WidgetConfig(-1);
app.appConfig.widgetTimetableConfigs.put(appWidgetId, widgetConfig);
app.appConfig.savePending = true;
}
RemoteViews views;
if (widgetConfig.bigStyle) {
views = new RemoteViews(context.getPackageName(), widgetConfig.darkTheme ? R.layout.widget_notifications_dark_big : R.layout.widget_notifications_big);
}
else {
views = new RemoteViews(context.getPackageName(), widgetConfig.darkTheme ? R.layout.widget_notifications_dark : R.layout.widget_notifications);
}
PorterDuff.Mode mode = PorterDuff.Mode.DST_IN;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
// this code seems to crash the launcher on >= P
float transparency = widgetConfig.opacity; //0...1
long colorFilter = 0x01000000L * (long) (255f * transparency);
try {
final Method[] declaredMethods = Class.forName("android.widget.RemoteViews").getDeclaredMethods();
final int len = declaredMethods.length;
if (len > 0) {
for (int m = 0; m < len; m++) {
final Method method = declaredMethods[m];
if (method.getName().equals("setDrawableParameters")) {
method.setAccessible(true);
method.invoke(views, R.id.widgetNotificationsListView, true, -1, (int) colorFilter, mode, -1);
method.invoke(views, R.id.widgetNotificationsHeader, true, -1, (int) colorFilter, mode, -1);
break;
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
Intent refreshIntent = new Intent(context, WidgetNotifications.class);
refreshIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
refreshIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
PendingIntent pendingRefreshIntent = PendingIntent.getBroadcast(context,
0, refreshIntent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.widgetNotificationsRefresh, pendingRefreshIntent);
views.setOnClickPendingIntent(R.id.widgetNotificationsSync, getPendingSelfIntent(context, ACTION_SYNC_DATA));
views.setImageViewBitmap(R.id.widgetNotificationsRefresh, new IconicsDrawable(context, CommunityMaterial.Icon2.cmd_refresh)
.color(IconicsColor.colorInt(Color.WHITE))
.size(IconicsSize.dp(widgetConfig.bigStyle ? 24 : 16)).toBitmap());
views.setImageViewBitmap(R.id.widgetNotificationsSync, new IconicsDrawable(context, CommunityMaterial.Icon.cmd_download_outline)
.color(IconicsColor.colorInt(Color.WHITE))
.size(IconicsSize.dp(widgetConfig.bigStyle ? 24 : 16)).toBitmap());
//d(TAG, "Profiles: "+ Arrays.toString(profileList.toArray()));
if (app.appConfig.notifications.size() == 0) {
views.setViewVisibility(R.id.widgetNotificationsLoading, View.VISIBLE);
views.setRemoteAdapter(R.id.widgetNotificationsListView, new Intent());
views.setTextViewText(R.id.widgetNotificationsLoading, app.getString(R.string.widget_notifications_no_data));
}
else {
views.setViewVisibility(R.id.widgetNotificationsLoading, View.GONE);
Intent listIntent = new Intent(context, WidgetNotificationsService.class);
listIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
listIntent.putExtra("bigStyle", widgetConfig.bigStyle);
listIntent.putExtra("darkTheme", widgetConfig.darkTheme);
listIntent.setData(Uri.parse(listIntent.toUri(Intent.URI_INTENT_SCHEME)));
views.setRemoteAdapter(R.id.widgetNotificationsListView, listIntent);
// template to handle the click listener for each item
Intent intentTemplate = new Intent(context, MainActivity.class);
intentTemplate.setAction("android.intent.action.MAIN");
PendingIntent pendingIntentNotifications = PendingIntent.getActivity(context, 0, intentTemplate, 0);
views.setPendingIntentTemplate(R.id.widgetNotificationsListView, pendingIntentNotifications);
}
Intent openIntent = new Intent(context, MainActivity.class);
openIntent.setAction("android.intent.action.MAIN");
openIntent.putExtra("fragmentId", MainActivity.DRAWER_ITEM_NOTIFICATIONS);
PendingIntent pendingOpenIntent = PendingIntent.getActivity(context,
appWidgetId, openIntent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.widgetNotificationsHeader, pendingOpenIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widgetNotificationsListView);
}
//modeInt++;
}
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
App app = (App) context.getApplicationContext();
for (int appWidgetId: appWidgetIds) {
app.appConfig.widgetTimetableConfigs.remove(appWidgetId);
}
app.saveConfig("widgetTimetableConfigs");
}
}

View File

@ -1,98 +0,0 @@
package pl.szczodrzynski.edziennik.widgets.notifications;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.models.Notification;
public class WidgetNotificationsListProvider implements RemoteViewsService.RemoteViewsFactory {
private static final String TAG = "WidgetNotificationsProv";
private App app;
private Context context;
private int appWidgetId;
private boolean bigStyle;
private boolean darkTheme;
//For obtaining the activity's context and intent
public WidgetNotificationsListProvider(Context context, Intent intent) {
this.app = (App) context.getApplicationContext();
this.context = context;
this.appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0);
this.bigStyle = intent.getBooleanExtra("bigStyle", false);
this.darkTheme = intent.getBooleanExtra("darkTheme", false);
// executed only ONCE
Log.d(TAG, "appWidgetId: "+appWidgetId);
}
@Override
public void onCreate() {
}
@Override
public void onDataSetChanged() {
// executed EVERY TIME
Log.d(TAG, "onDataSetChanged for appWidgetId: "+appWidgetId);
}
@Override
public void onDestroy() {
}
@Override
public int getCount() {
return (app.appConfig.notifications == null ? 0 : app.appConfig.notifications.size());
}
@Override
public RemoteViews getViewAt(int i) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.row_widget_notifications_item);
if (i > app.appConfig.notifications.size()-1)
return views;
Notification notification = app.appConfig.notifications.get(i);
if (bigStyle) {
views = new RemoteViews(context.getPackageName(), darkTheme ? R.layout.row_widget_notifications_dark_big_item : R.layout.row_widget_notifications_big_item);
}
else if (darkTheme) {
views = new RemoteViews(context.getPackageName(), R.layout.row_widget_notifications_dark_item);
}
Intent intent = new Intent();
notification.fillIntent(intent);
views.setOnClickFillInIntent(R.id.widgetNotificationsRoot, intent);
views.setTextViewText(R.id.widgetNotificationsTitle, app.getString(R.string.widget_notifications_title_format, notification.title, Notification.stringType(context, notification.type)));
views.setTextViewText(R.id.widgetNotificationsText, notification.text);
views.setTextViewText(R.id.widgetNotificationsDate, Date.fromMillis(notification.addedDate).getFormattedString());
return views;
}
@Override
public RemoteViews getLoadingView() {
return null;
}
@Override
public int getViewTypeCount() {
return 1;
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public boolean hasStableIds() {
return true;
}
}

View File

@ -1,11 +0,0 @@
package pl.szczodrzynski.edziennik.widgets.notifications;
import android.content.Intent;
import android.widget.RemoteViewsService;
public class WidgetNotificationsService extends RemoteViewsService {
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return (new WidgetNotificationsListProvider(this.getApplicationContext(), intent));
}
}

View File

@ -1,17 +0,0 @@
package pl.szczodrzynski.edziennik.widgets.timetable;
import android.content.Intent;
import android.widget.RemoteViewsService;
public class WidgetTimetableService extends RemoteViewsService {
/*
* So pretty simple just defining the Adapter of the listview
* here Adapter is ListProvider
* */
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return (new WidgetTimetableListProvider(this.getApplicationContext(), intent));
}
}

View File

@ -1,6 +1,4 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -36,14 +34,6 @@
</LinearLayout>
<ImageButton
android:id="@+id/widgetNotificationsRefresh"
android:layout_width="45.0dip"
android:layout_height="45.0dip"
android:layout_gravity="end|center"
android:background="?android:selectableItemBackground"
android:contentDescription="@string/widget_refresh" />
<ImageButton
android:id="@+id/widgetNotificationsSync"
android:layout_width="45.0dip"

View File

@ -1,6 +1,4 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -36,14 +34,6 @@
</LinearLayout>
<ImageButton
android:id="@+id/widgetNotificationsRefresh"
android:layout_width="60dip"
android:layout_height="60dip"
android:layout_gravity="end|center"
android:background="?android:selectableItemBackground"
android:contentDescription="@string/widget_refresh" />
<ImageButton
android:id="@+id/widgetNotificationsSync"
android:layout_width="60dip"

View File

@ -1,6 +1,4 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -36,14 +34,6 @@
</LinearLayout>
<ImageButton
android:id="@+id/widgetNotificationsRefresh"
android:layout_width="45.0dip"
android:layout_height="45.0dip"
android:layout_gravity="end|center"
android:background="?android:selectableItemBackground"
android:contentDescription="@string/widget_refresh" />
<ImageButton
android:id="@+id/widgetNotificationsSync"
android:layout_width="45.0dip"

View File

@ -1,6 +1,4 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -36,14 +34,6 @@
</LinearLayout>
<ImageButton
android:id="@+id/widgetNotificationsRefresh"
android:layout_width="60dip"
android:layout_height="60dip"
android:layout_gravity="end|center"
android:background="?android:selectableItemBackground"
android:contentDescription="@string/widget_refresh" />
<ImageButton
android:id="@+id/widgetNotificationsSync"
android:layout_width="60dip"

View File

@ -11,5 +11,5 @@
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="1800000"
android:widgetCategory="home_screen"
android:configure="pl.szczodrzynski.edziennik.widgets.WidgetConfigActivity"
android:configure="pl.szczodrzynski.edziennik.ui.widgets.WidgetConfigActivity"
tools:ignore="UnusedAttribute" />

View File

@ -11,5 +11,5 @@
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="5400000"
android:widgetCategory="home_screen"
android:configure="pl.szczodrzynski.edziennik.widgets.WidgetConfigActivity"
android:configure="pl.szczodrzynski.edziennik.ui.widgets.WidgetConfigActivity"
tools:ignore="UnusedAttribute" />

View File

@ -11,5 +11,5 @@
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="1800000"
android:widgetCategory="home_screen"
android:configure="pl.szczodrzynski.edziennik.widgets.WidgetConfigActivity"
android:configure="pl.szczodrzynski.edziennik.ui.widgets.WidgetConfigActivity"
tools:ignore="UnusedAttribute" />