diff --git a/app/build.gradle b/app/build.gradle index 82415f9e..fbb6c42d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,7 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } debug { + applicationIdSuffix ".debug" minifyEnabled true shrinkResources false } @@ -49,21 +50,24 @@ android { dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.8.0' + implementation 'com.google.android.material:material:1.9.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation 'androidx.navigation:navigation-fragment:2.5.3' - implementation 'androidx.navigation:navigation-ui:2.5.3' - implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' - implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1' - implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1' - implementation 'androidx.core:core-splashscreen:1.0.0' + implementation 'androidx.navigation:navigation-fragment-ktx:2.6.0' + implementation 'androidx.navigation:navigation-ui-ktx:2.6.0' + implementation 'androidx.core:core-splashscreen:1.0.1' implementation 'com.android.volley:volley:1.2.1' - implementation 'androidx.core:core-ktx:1.10.0' + implementation 'androidx.core:core-ktx:1.10.1' implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" testImplementation 'junit:junit:4.+' - androidTestImplementation 'androidx.test.ext:junit:1.1.4' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + + // Lifecycle Components + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1' + implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' + implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.1' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1' /** */ implementation 'com.google.code.gson:gson:2.9.0' @@ -73,7 +77,7 @@ dependencies { implementation 'com.github.antonKozyriatskyi:CircularProgressIndicator:1.3.0' implementation 'fr.bmartel:jspeedtest:1.32.1' implementation 'io.ipinfo:ipinfo-api:2.1' - implementation 'com.squareup.okhttp3:okhttp:4.9.3' + implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'com.github.bumptech.glide:glide:4.15.1' implementation "androidx.work:work-runtime:2.8.1" implementation "androidx.work:work-runtime-ktx:2.8.1" diff --git a/app/src/main/java/com/drnoob/datamonitor/adapters/UsageDataAdapter.kt b/app/src/main/java/com/drnoob/datamonitor/adapters/UsageDataAdapter.kt new file mode 100644 index 00000000..439bec9a --- /dev/null +++ b/app/src/main/java/com/drnoob/datamonitor/adapters/UsageDataAdapter.kt @@ -0,0 +1,105 @@ +package com.drnoob.datamonitor.adapters + +import android.content.Context +import android.content.pm.PackageManager +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.AsyncListDiffer +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView +import com.drnoob.datamonitor.Common +import com.drnoob.datamonitor.R +import com.drnoob.datamonitor.adapters.data.AppDataUsageModel +import com.drnoob.datamonitor.utils.NetworkStatsHelper +import com.skydoves.progressview.ProgressView + +class UsageDataAdapter(private val context: Context) : + RecyclerView.Adapter() { + + inner class UsageDataViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) + + private val differCallbacks = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: AppDataUsageModel, + newItem: AppDataUsageModel + ): Boolean { + return oldItem.packageName == newItem.packageName + && oldItem.totalDataUsage == newItem.totalDataUsage + } + + override fun areContentsTheSame( + oldItem: AppDataUsageModel, + newItem: AppDataUsageModel + ): Boolean { + return oldItem.totalDataUsage == newItem.totalDataUsage + && oldItem.session == newItem.session + } + } + + val differ = AsyncListDiffer(this, differCallbacks) + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): UsageDataAdapter.UsageDataViewHolder { + return UsageDataViewHolder( + LayoutInflater.from(parent.context) + .inflate(R.layout.app_data_usage_item, parent, false) + ) + } + + override fun onBindViewHolder(holder: UsageDataAdapter.UsageDataViewHolder, position: Int) { + + val model = differ.currentList[position] + val itemView = holder.itemView + + val mAppIcon = itemView.findViewById(R.id.app_icon) + val mAppName = itemView.findViewById(R.id.app_name) + val mDataUsage = itemView.findViewById(R.id.data_usage) + val mProgress = itemView.findViewById(R.id.progress) + + try { + if (model.packageName == "com.android.tethering") + mAppIcon.setImageResource(R.drawable.hotspot) + else if (model.packageName == "com.android.deleted") + mAppIcon.setImageResource(R.drawable.deleted_apps) + else + if (Common.isAppInstalled(context, model.packageName)) + mAppIcon.setImageDrawable( + context.packageManager.getApplicationIcon(model.packageName) + ) + else mAppIcon.setImageResource(R.drawable.deleted_apps) + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + } + + val totalDataUsage = + NetworkStatsHelper.formatData(model.sentMobile, model.receivedMobile)[2] + + if (model.progress > 0) mProgress.progress = model.progress.toFloat() + else mProgress.progress = 1F + + mAppName.text = model.appName + mDataUsage.text = totalDataUsage + + itemView.setOnClickListener { + onItemClickListener?.let { + if (model != null) it(model) + } + } + + } + + override fun getItemCount(): Int = + differ.currentList.size + + private var onItemClickListener: ((AppDataUsageModel) -> Unit)? = null + + fun setOnItemClickListener(listener: (AppDataUsageModel) -> Unit) { + onItemClickListener = listener + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/drnoob/datamonitor/adapters/data/DataUsageViewModel.kt b/app/src/main/java/com/drnoob/datamonitor/adapters/data/DataUsageViewModel.kt new file mode 100644 index 00000000..c81ba65e --- /dev/null +++ b/app/src/main/java/com/drnoob/datamonitor/adapters/data/DataUsageViewModel.kt @@ -0,0 +1,46 @@ +package com.drnoob.datamonitor.adapters.data + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope +import com.drnoob.datamonitor.utils.helpers.UsageDataHelper +import kotlinx.coroutines.launch + +class DataUsageViewModel(private val usageDataHelper: UsageDataHelper) : ViewModel() { + + private val _userAppsList: MutableLiveData> = MutableLiveData() + val userAppsList: LiveData> + get() = _userAppsList + + private val _systemAppsList: MutableLiveData> = MutableLiveData() + val systemAppsList: LiveData> + get() = _userAppsList + + fun fetchApps() = viewModelScope.launch { + usageDataHelper.fetchApps() + } + + fun loadUserAppsData(session: Int, type: Int) = viewModelScope.launch { + usageDataHelper.fetchApps() + _userAppsList.postValue(usageDataHelper.loadUserAppsData(session, type)) + } + + fun loadSystemAppsData(session: Int, type: Int) = viewModelScope.launch { + usageDataHelper.fetchApps() + _systemAppsList.postValue(usageDataHelper.loadSystemAppsData(session, type)) + } + +} + +class DataUsageViewModelFactory(private val usageDataHelper: UsageDataHelper) : + ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(DataUsageViewModel::class.java)) { + @Suppress("UNCHECKED_CAST") + return DataUsageViewModel(usageDataHelper) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/drnoob/datamonitor/ui/activities/AppPickerActivity.java b/app/src/main/java/com/drnoob/datamonitor/ui/activities/AppPickerActivity.java index f4d988c8..97d7171a 100644 --- a/app/src/main/java/com/drnoob/datamonitor/ui/activities/AppPickerActivity.java +++ b/app/src/main/java/com/drnoob/datamonitor/ui/activities/AppPickerActivity.java @@ -50,6 +50,7 @@ import com.drnoob.datamonitor.databinding.ActivityAppPickerBinding; import com.drnoob.datamonitor.utils.CrashReporter; import com.drnoob.datamonitor.utils.SharedPreferences; +import com.drnoob.datamonitor.utils.helpers.ThemeHelperKt; import com.google.android.material.elevation.SurfaceColors; import java.util.ArrayList; @@ -80,7 +81,7 @@ public static void setData(Intent data) { @Override protected void onCreate(Bundle savedInstanceState) { - MainActivity.setTheme(AppPickerActivity.this); + ThemeHelperKt.setTheme(AppPickerActivity.this); Thread.setDefaultUncaughtExceptionHandler(new CrashReporter(AppPickerActivity.this)); String languageCode = SharedPreferences.getUserPrefs(this).getString(APP_LANGUAGE_CODE, "null"); String countryCode = SharedPreferences.getUserPrefs(this).getString(APP_COUNTRY_CODE, ""); diff --git a/app/src/main/java/com/drnoob/datamonitor/ui/activities/ContainerActivity.java b/app/src/main/java/com/drnoob/datamonitor/ui/activities/ContainerActivity.java index af4d4e93..10915d6a 100644 --- a/app/src/main/java/com/drnoob/datamonitor/ui/activities/ContainerActivity.java +++ b/app/src/main/java/com/drnoob/datamonitor/ui/activities/ContainerActivity.java @@ -79,6 +79,7 @@ import com.drnoob.datamonitor.ui.fragments.SystemDataUsageFragment; import com.drnoob.datamonitor.utils.CrashReporter; import com.drnoob.datamonitor.utils.SharedPreferences; +import com.drnoob.datamonitor.utils.helpers.ThemeHelperKt; import com.google.android.material.elevation.SurfaceColors; import org.jetbrains.annotations.NotNull; @@ -97,7 +98,7 @@ public class ContainerActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { - MainActivity.setTheme(ContainerActivity.this); + ThemeHelperKt.setTheme(ContainerActivity.this); Thread.setDefaultUncaughtExceptionHandler(new CrashReporter(ContainerActivity.this)); String languageCode = SharedPreferences.getUserPrefs(this).getString(APP_LANGUAGE_CODE, "null"); String countryCode = SharedPreferences.getUserPrefs(this).getString(APP_COUNTRY_CODE, ""); diff --git a/app/src/main/java/com/drnoob/datamonitor/ui/activities/CrashReportActivity.java b/app/src/main/java/com/drnoob/datamonitor/ui/activities/CrashReportActivity.java index 39212164..ac26659e 100644 --- a/app/src/main/java/com/drnoob/datamonitor/ui/activities/CrashReportActivity.java +++ b/app/src/main/java/com/drnoob/datamonitor/ui/activities/CrashReportActivity.java @@ -48,6 +48,7 @@ import com.drnoob.datamonitor.core.base.Preference; import com.drnoob.datamonitor.databinding.ActivityCrashReportBinding; import com.drnoob.datamonitor.utils.SharedPreferences; +import com.drnoob.datamonitor.utils.helpers.ThemeHelperKt; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.elevation.SurfaceColors; import com.google.android.material.snackbar.Snackbar; @@ -71,7 +72,7 @@ public class CrashReportActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { - MainActivity.setTheme(CrashReportActivity.this); + ThemeHelperKt.setTheme(CrashReportActivity.this); String languageCode = SharedPreferences.getUserPrefs(this).getString(APP_LANGUAGE_CODE, "null"); String countryCode = SharedPreferences.getUserPrefs(this).getString(APP_COUNTRY_CODE, ""); if (languageCode.equals("null")) { diff --git a/app/src/main/java/com/drnoob/datamonitor/ui/activities/MainActivity.java b/app/src/main/java/com/drnoob/datamonitor/ui/activities/MainActivity.java deleted file mode 100644 index 2216049d..00000000 --- a/app/src/main/java/com/drnoob/datamonitor/ui/activities/MainActivity.java +++ /dev/null @@ -1,984 +0,0 @@ -/* - * Copyright (C) 2021 Dr.NooB - * - * This file is a part of Data Monitor - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.drnoob.datamonitor.ui.activities; - -import static com.drnoob.datamonitor.Common.isAppInstalled; -import static com.drnoob.datamonitor.Common.isReadPhoneStateGranted; -import static com.drnoob.datamonitor.Common.isUsageAccessGranted; -import static com.drnoob.datamonitor.Common.refreshService; -import static com.drnoob.datamonitor.Common.setLanguage; -import static com.drnoob.datamonitor.Common.showAlarmPermissionDeniedDialog; -import static com.drnoob.datamonitor.core.Values.ALARM_PERMISSION_DENIED; -import static com.drnoob.datamonitor.core.Values.APP_COUNTRY_CODE; -import static com.drnoob.datamonitor.core.Values.APP_DATA_USAGE_WARNING_CHANNEL_ID; -import static com.drnoob.datamonitor.core.Values.APP_DATA_USAGE_WARNING_CHANNEL_NAME; -import static com.drnoob.datamonitor.core.Values.APP_LANGUAGE_CODE; -import static com.drnoob.datamonitor.core.Values.APP_THEME; -import static com.drnoob.datamonitor.core.Values.BOTTOM_NAVBAR_ITEM_SETTINGS; -import static com.drnoob.datamonitor.core.Values.DATA_RESET_DATE; -import static com.drnoob.datamonitor.core.Values.DATA_USAGE_NOTIFICATION_CHANNEL_ID; -import static com.drnoob.datamonitor.core.Values.DATA_USAGE_NOTIFICATION_CHANNEL_NAME; -import static com.drnoob.datamonitor.core.Values.DATA_USAGE_SYSTEM; -import static com.drnoob.datamonitor.core.Values.DATA_USAGE_VALUE; -import static com.drnoob.datamonitor.core.Values.DATA_USAGE_WARNING_CHANNEL_ID; -import static com.drnoob.datamonitor.core.Values.DATA_USAGE_WARNING_CHANNEL_NAME; -import static com.drnoob.datamonitor.core.Values.DISABLE_BATTERY_OPTIMISATION_FRAGMENT; -import static com.drnoob.datamonitor.core.Values.GENERAL_FRAGMENT_ID; -import static com.drnoob.datamonitor.core.Values.NETWORK_SIGNAL_CHANNEL_ID; -import static com.drnoob.datamonitor.core.Values.NETWORK_SIGNAL_CHANNEL_NAME; -import static com.drnoob.datamonitor.core.Values.OTHER_NOTIFICATION_CHANNEL_ID; -import static com.drnoob.datamonitor.core.Values.OTHER_NOTIFICATION_CHANNEL_NAME; -import static com.drnoob.datamonitor.core.Values.READ_PHONE_STATE_DISABLED; -import static com.drnoob.datamonitor.core.Values.REQUEST_POST_NOTIFICATIONS; -import static com.drnoob.datamonitor.core.Values.SESSION_TODAY; -import static com.drnoob.datamonitor.core.Values.SETUP_COMPLETED; -import static com.drnoob.datamonitor.core.Values.SETUP_VALUE; -import static com.drnoob.datamonitor.core.Values.SHOULD_SHOW_BATTERY_OPTIMISATION_ERROR; -import static com.drnoob.datamonitor.core.Values.TYPE_MOBILE_DATA; -import static com.drnoob.datamonitor.core.Values.UPDATE_NOTIFICATION_CHANNEL; -import static com.drnoob.datamonitor.core.Values.UPDATE_VERSION; -import static com.drnoob.datamonitor.core.Values.USAGE_ACCESS_DISABLED; -import static com.drnoob.datamonitor.ui.fragments.AppDataUsageFragment.getAppContext; -import static com.drnoob.datamonitor.ui.fragments.AppDataUsageFragment.onDataLoaded; -import static com.drnoob.datamonitor.utils.NetworkStatsHelper.getAppMobileDataUsage; -import static com.drnoob.datamonitor.utils.NetworkStatsHelper.getAppWifiDataUsage; -import static com.drnoob.datamonitor.utils.NetworkStatsHelper.getDeletedAppsMobileDataUsage; -import static com.drnoob.datamonitor.utils.NetworkStatsHelper.getDeletedAppsWifiDataUsage; -import static com.drnoob.datamonitor.utils.NetworkStatsHelper.getDeviceMobileDataUsage; -import static com.drnoob.datamonitor.utils.NetworkStatsHelper.getDeviceWifiDataUsage; -import static com.drnoob.datamonitor.utils.NetworkStatsHelper.getTetheringDataUsage; - -import android.Manifest; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.app.Activity; -import android.app.AlarmManager; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.appwidget.AppWidgetManager; -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.media.AudioAttributes; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.os.PowerManager; -import android.os.RemoteException; -import android.provider.Settings; -import android.text.Spannable; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.RemoteViews; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.annotation.RequiresApi; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.app.AppCompatDelegate; -import androidx.constraintlayout.widget.ConstraintLayout; -import androidx.core.app.NotificationCompat; -import androidx.navigation.NavController; -import androidx.navigation.NavDestination; -import androidx.navigation.fragment.NavHostFragment; -import androidx.navigation.ui.NavigationUI; -import androidx.preference.PreferenceManager; - -import com.drnoob.datamonitor.BuildConfig; -import com.drnoob.datamonitor.R; -import com.drnoob.datamonitor.Widget.DataUsageWidget; -import com.drnoob.datamonitor.adapters.data.AppDataUsageModel; -import com.drnoob.datamonitor.core.task.DatabaseHandler; -import com.drnoob.datamonitor.databinding.ActivityMainBinding; -import com.drnoob.datamonitor.utils.CrashReporter; -import com.drnoob.datamonitor.utils.SharedPreferences; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import com.google.android.material.elevation.SurfaceColors; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -public class MainActivity extends AppCompatActivity { - private static final String TAG = MainActivity.class.getSimpleName(); - ActivityMainBinding binding; - - public static List mAppsList = new ArrayList<>(); - public static List mUserAppsList = new ArrayList<>(); - public static List mSystemAppsList = new ArrayList<>(); - public static int value; - public static String themeSwitch; - private static Boolean isDataLoading = false; - private static Boolean refreshAppDataUsage = false; - - public static Boolean getRefreshAppDataUsage() { - return refreshAppDataUsage; - } - - public static void setRefreshAppDataUsage(Boolean refreshAppDataUsage) { - MainActivity.refreshAppDataUsage = refreshAppDataUsage; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - MainActivity.setTheme(MainActivity.this); - Thread.setDefaultUncaughtExceptionHandler(new CrashReporter(MainActivity.this)); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { - if (!isReadPhoneStateGranted(MainActivity.this)) { - startActivity(new Intent(this, SetupActivity.class) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) - .putExtra(SETUP_VALUE, READ_PHONE_STATE_DISABLED)); - finish(); - } - } - super.onCreate(savedInstanceState); - String languageCode = SharedPreferences.getUserPrefs(this).getString(APP_LANGUAGE_CODE, "null"); - String countryCode = SharedPreferences.getUserPrefs(this).getString(APP_COUNTRY_CODE, ""); - if (languageCode.equals("null")) { - setLanguage(this, "en", countryCode); - } else { - setLanguage(this, languageCode, countryCode); - } - - try { - refreshService(this); - } catch (Exception e) { - e.printStackTrace(); - } - try { - if (isUsageAccessGranted(MainActivity.this)) { - - binding = ActivityMainBinding.inflate(getLayoutInflater()); - setTheme(R.style.Theme_DataMonitor); - setContentView(binding.getRoot()); - setSupportActionBar(binding.mainToolbar); - binding.mainToolbar.setBackgroundColor(SurfaceColors.SURFACE_2.getColor(this)); - getWindow().setStatusBarColor(SurfaceColors.SURFACE_2.getColor(this)); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { - getWindow().setNavigationBarColor(SurfaceColors.SURFACE_2.getColor(this)); - } - - SharedPreferences.getUserPrefs(this).edit().putBoolean(SETUP_COMPLETED, true).apply(); - - if (binding.bottomNavigationView.getSelectedItemId() == R.id.bottom_menu_home) { - getSupportActionBar().setTitle(getString(R.string.app_name)); - } - - NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.main_nav_host_fragment); - NavController controller = navHostFragment.getNavController(); - controller.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() { - @Override - public void onDestinationChanged(@NotNull NavController navController, @NotNull NavDestination navDestination, @Nullable Bundle bundle) { - changeBanner(navDestination); - } - }); // working - - NavigationUI.setupWithNavController(binding.bottomNavigationView, controller); - - DatabaseHandler databaseHandler = new DatabaseHandler(MainActivity.this); - if (databaseHandler.getUsageList() != null && databaseHandler.getUsageList().size() > 0) { - - } else { - MainActivity.FetchApps fetchApps = new MainActivity.FetchApps(this); - fetchApps.execute(); - - } - - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - createNotificationChannel(); - } - - value = getIntent().getIntExtra(DATA_USAGE_VALUE, 0); - - if (value == DATA_USAGE_SYSTEM) { - binding.bottomNavigationView.setVisibility(View.GONE); - binding.bottomNavigationView.setSelectedItemId(R.id.bottom_menu_app_data_usage); - getSupportActionBar().setTitle(R.string.system_data_usage); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_arrow); - controller.navigate(R.id.system_data_usage); // >> working - } - if (value != DATA_USAGE_SYSTEM) { - if (!isDataLoading()) { - MainActivity.LoadData loadData = new MainActivity.LoadData(MainActivity.this, SESSION_TODAY, TYPE_MOBILE_DATA); - loadData.execute(); - } - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - NotificationManager notificationManager = getSystemService(NotificationManager.class); - if (!notificationManager.areNotificationsEnabled()) { - requestPermissions(new String[]{Manifest.permission.POST_NOTIFICATIONS}, REQUEST_POST_NOTIFICATIONS); - } - } - } else { - onResume(); - } - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } catch (Exception e) { - e.printStackTrace(); - } - - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (requestCode == REQUEST_POST_NOTIFICATIONS) { - if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) { - new MaterialAlertDialogBuilder(MainActivity.this) - .setTitle(R.string.label_permission_denied) - .setMessage(R.string.notification_permission_denied_body) - .setPositiveButton(R.string.action_grant, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - Intent intent = new Intent(); - intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS"); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra("app_package", getPackageName()); - intent.putExtra("app_uid", getApplicationInfo().uid); - intent.putExtra("android.provider.extra.APP_PACKAGE", getPackageName()); - - startActivity(intent); - } - }) - .setNegativeButton(R.string.action_cancel, null) - .show(); - } - } - } - - private void checkBatteryOptimisationState() { - PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); - if (powerManager.isIgnoringBatteryOptimizations(getPackageName())) { - // Battery optimisation is disabled - Log.d(TAG, "checkBatteryOptimisationState: Disabled"); - } else { - // Battery optimisation is enabled - Log.d(TAG, "checkBatteryOptimisationState: Enabled"); - if (SharedPreferences.getUserPrefs(this).getBoolean(SHOULD_SHOW_BATTERY_OPTIMISATION_ERROR, true)) { - new MaterialAlertDialogBuilder(this) - .setTitle(getString(R.string.label_battery_optimisation)) - .setMessage(getString(R.string.battery_optimisation_enabled_info)) - .setPositiveButton(getString(R.string.disable_battery_optimisation), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - startActivity(new Intent(MainActivity.this, ContainerActivity.class) - .putExtra(GENERAL_FRAGMENT_ID, DISABLE_BATTERY_OPTIMISATION_FRAGMENT)); - } - }) - .setNegativeButton(getString(R.string.label_do_not_show_again), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - SharedPreferences.getUserPrefs(MainActivity.this).edit() - .putBoolean(SHOULD_SHOW_BATTERY_OPTIMISATION_ERROR, false) - .apply(); - dialog.dismiss(); - } - }) - .setNeutralButton(getString(R.string.action_cancel), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }) - .show(); - } - } - } - - private void initializebottomNavigationViewBar() { - NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.main_nav_host_fragment); - NavController controller = navHostFragment.getNavController(); - controller.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() { - @Override - public void onDestinationChanged(@NotNull NavController navController, @NotNull NavDestination navDestination, @Nullable Bundle bundle) { - changeBanner(navDestination); - } - }); // working - -// NavigationUI.setupWithNavController(binding.bottomNavigationViewigationView, controller); // working - } - - - private void changeBanner(NavDestination navDestination) { - String destination = navDestination.getLabel().toString(); - Spannable banner; - - if (destination.equalsIgnoreCase(getString(R.string.home))) { - // Home Fragment - getSupportActionBar().setTitle(getString(R.string.app_name)); - } else if (destination.equalsIgnoreCase(getString(R.string.setup))) { - // Setup Fragment - getSupportActionBar().setTitle(getString(R.string.setup)); - } else if (destination.equalsIgnoreCase(getString(R.string.app_data_usage))) { - // App data usage Fragment - getSupportActionBar().setTitle(getString(R.string.app_data_usage)); - } else if (destination.equalsIgnoreCase(getString(R.string.network_diagnostics))) { - // Network diagnostics Fragment - getSupportActionBar().setTitle(getString(R.string.network_diagnostics)); - } else { - // Unknown Fragment - } - - } - - - @Override - protected void onStart() { - super.onStart(); - verifyAppVersion(); -// initializebottomNavigationViewBar(); - - if (!PreferenceManager.getDefaultSharedPreferences(MainActivity.this) - .getBoolean(ALARM_PERMISSION_DENIED, false)) { - AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - if (!alarmManager.canScheduleExactAlarms()) { - new MaterialAlertDialogBuilder(this) - .setTitle(getString(R.string.error_alarm_permission_denied)) - .setMessage(getString(R.string.error_alarm_permission_denied_dialog_summary)) - .setCancelable(false) - .setPositiveButton(getString(R.string.action_grant), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - Intent intent = new Intent(); - intent.setAction(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - dialog.dismiss(); - startActivity(intent); - } - }) - .setNegativeButton(getString(R.string.action_cancel), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }) - .setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(DialogInterface dialog) { - PreferenceManager.getDefaultSharedPreferences(MainActivity.this).edit() - .putBoolean(ALARM_PERMISSION_DENIED, true) - .apply(); - } - }) - .show(); - } - } - } - } - - @Override - public void onBackPressed() { - if (value == DATA_USAGE_SYSTEM) { - value = 0; - finish(); - } else { - super.onBackPressed(); - binding.bottomNavigationView.setSelectedItemId(R.id.bottom_menu_home); - } - } - - @Override - protected void onResume() { - super.onResume(); - try { - if (!isUsageAccessGranted(MainActivity.this)) { - startActivity(new Intent(this, SetupActivity.class) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) - .putExtra(SETUP_VALUE, USAGE_ACCESS_DISABLED)); - } - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { - if (!isReadPhoneStateGranted(MainActivity.this)) { - startActivity(new Intent(this, SetupActivity.class) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) - .putExtra(SETUP_VALUE, READ_PHONE_STATE_DISABLED)); - } - } - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } - try { - checkBatteryOptimisationState(); - } catch (Exception e) { - e.printStackTrace(); - } - - // Action bar title resets while changing theme in settings, setting current title -// NavController controller = Navigation.findNavController(this, R.id.main_nav_host_fragment); -// if (controller.getCurrentDestination().getId() == R.id.bottom_menu_settings) { -// getSupportActionBar().setTitle(getString(R.string.settings)); -// } - - } - - @Override - protected void onDestroy() { - Intent intent = new Intent(MainActivity.this, DataUsageWidget.class); - intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); - int[] ids = AppWidgetManager.getInstance(MainActivity.this) - .getAppWidgetIds(new ComponentName(MainActivity.this, DataUsageWidget.class)); - AppWidgetManager.getInstance(this).updateAppWidget(ids, new RemoteViews(getPackageName(), R.layout.data_usage_widget)); - super.onDestroy(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.toolbar_menu, menu); - return super.onCreateOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(@NonNull @NotNull MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - if (value == DATA_USAGE_SYSTEM) { - value = 0; - finish(); - } - break; - - case R.id.toolbar_settings: - startActivity(new Intent(MainActivity.this, ContainerActivity.class) - .putExtra(GENERAL_FRAGMENT_ID, BOTTOM_NAVBAR_ITEM_SETTINGS)); -// startActivity(new Intent(MainActivity.this, MainActivity.class)); - break; - } - return super.onOptionsItemSelected(item); - } - - @RequiresApi(api = Build.VERSION_CODES.O) - private void createNotificationChannel() { - NotificationChannel usageChannel = new NotificationChannel(DATA_USAGE_NOTIFICATION_CHANNEL_ID, - DATA_USAGE_NOTIFICATION_CHANNEL_NAME, - NotificationManager.IMPORTANCE_LOW); - NotificationChannel warningChannel = new NotificationChannel(DATA_USAGE_WARNING_CHANNEL_ID, DATA_USAGE_WARNING_CHANNEL_NAME, - NotificationManager.IMPORTANCE_HIGH); - NotificationChannel appWarningChannel = new NotificationChannel(APP_DATA_USAGE_WARNING_CHANNEL_ID, APP_DATA_USAGE_WARNING_CHANNEL_NAME, - NotificationManager.IMPORTANCE_HIGH); - NotificationChannel networkSignalChannel = new NotificationChannel(NETWORK_SIGNAL_CHANNEL_ID, NETWORK_SIGNAL_CHANNEL_NAME, - NotificationManager.IMPORTANCE_HIGH); - NotificationChannel otherChannel = new NotificationChannel(OTHER_NOTIFICATION_CHANNEL_ID, OTHER_NOTIFICATION_CHANNEL_NAME, - NotificationManager.IMPORTANCE_HIGH); - warningChannel.enableVibration(true); - warningChannel.enableLights(true); - appWarningChannel.enableVibration(true); - appWarningChannel.enableLights(true); - Uri sound = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + getPackageName() + "/" + R.raw.silent); - AudioAttributes attributes = new AudioAttributes.Builder() - .setUsage(AudioAttributes.USAGE_NOTIFICATION) - .build(); -// networkSignalChannel.setSound(sound, attributes); - networkSignalChannel.setSound(Uri.EMPTY, null); - networkSignalChannel.setShowBadge(false); - networkSignalChannel.enableVibration(false); - networkSignalChannel.enableLights(false); - networkSignalChannel.setBypassDnd(true); - otherChannel.enableVibration(true); - otherChannel.enableLights(true); - - List channels = new ArrayList<>(); - channels.add(usageChannel); - channels.add(warningChannel); - channels.add(appWarningChannel); - channels.add(networkSignalChannel); - channels.add(otherChannel); - - - NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); - if (PreferenceManager.getDefaultSharedPreferences(MainActivity.this) - .getBoolean(UPDATE_NOTIFICATION_CHANNEL, true)) { - notificationManager.deleteNotificationChannel("NetworkSignal.Notification"); - PreferenceManager.getDefaultSharedPreferences(MainActivity.this).edit() - .putBoolean(UPDATE_NOTIFICATION_CHANNEL, false) - .apply(); - } - notificationManager.createNotificationChannels(channels); - } - - protected void disableSelectedItem(int selectedItemIndex) { - for (int i = 0; i <= 3; i++) { - if (i == selectedItemIndex) { - binding.bottomNavigationView.getMenu().getItem(selectedItemIndex).setEnabled(false); - } else { - binding.bottomNavigationView.getMenu().getItem(i).setEnabled(true); - } - } - } - - private static class FetchApps extends AsyncTask { - private final Context mContext; - - public FetchApps(Context mContext) { - this.mContext = mContext; - } - - @Override - protected Object doInBackground(Object[] objects) { - Log.d(TAG, "doInBackground: checking applications"); - PackageManager packageManager = mContext.getPackageManager(); - List allApps = packageManager.getInstalledApplications(PackageManager.GET_META_DATA); - List modelList = new ArrayList<>(); - AppDataUsageModel model = null; - DatabaseHandler databaseHandler = new DatabaseHandler(mContext); - - for (ApplicationInfo applicationInfo : allApps) { - if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 1) { - // System app - modelList.add(new AppDataUsageModel(packageManager.getApplicationLabel(applicationInfo).toString(), - applicationInfo.packageName, - applicationInfo.uid, - true)); - } else { - // User app - modelList.add(new AppDataUsageModel(packageManager.getApplicationLabel(applicationInfo).toString(), - applicationInfo.packageName, - applicationInfo.uid, - false)); - } - } - - for (int i = 0; i < modelList.size(); i++) { - model = new AppDataUsageModel(); - model.setAppName(modelList.get(i).getAppName()); - model.setPackageName(modelList.get(i).getPackageName()); - model.setUid(modelList.get(i).getUid()); - model.setIsSystemApp(modelList.get(i).isSystemApp()); - - databaseHandler.addData(model); - } - - return null; - } - } - - public static List getAppsList() { - return mAppsList; - } - - public static Boolean isDataLoading() { - return isDataLoading; - } - - public static void setIsDataLoading(Boolean isDataLoading) { - MainActivity.isDataLoading = isDataLoading; - } - - public static class LoadData extends AsyncTask { - private final Context mContext; - private final int session; - private final int type; - private int date; - - public LoadData(Context mContext, int session, int type) { - this.mContext = mContext; - this.session = session; - this.type = type; - } - - - @Override - protected void onPreExecute() { - super.onPreExecute(); - isDataLoading = true; - mUserAppsList.clear(); - mSystemAppsList.clear(); - Log.d(TAG, "onPreExecute: load data"); - } - - @Override - protected Object doInBackground(Object[] objects) { - Long sent = 0L, - systemSent = 0L, - received = 0L, - systemReceived = 0L, - totalSystemSent = 0L, - totalSystemReceived = 0L, - totalTetheringSent = 0L, - totalTetheringReceived = 0L, - totalDeletedAppsSent = 0L, - totalDeletedAppsReceived = 0L, - tetheringTotal = 0L, - deletedAppsTotal = 0L; - - date = PreferenceManager.getDefaultSharedPreferences(mContext).getInt(DATA_RESET_DATE, 1); - - DatabaseHandler handler = new DatabaseHandler(mContext); - List list = handler.getUsageList(); - AppDataUsageModel model = null; - - for (int i = 0; i < list.size(); i++) { - AppDataUsageModel currentData = list.get(i); - if (currentData.isSystemApp()) { - if (type == TYPE_MOBILE_DATA) { - try { - sent = getAppMobileDataUsage(mContext, currentData.getUid(), session)[0]; - received = getAppMobileDataUsage(mContext, currentData.getUid(), session)[1]; - totalSystemSent = totalSystemSent + sent; - totalSystemReceived = totalSystemReceived + received; - - if (sent > 0 || received > 0) { - model = new AppDataUsageModel(); - model.setAppName(currentData.getAppName()); - model.setPackageName(currentData.getPackageName()); - model.setUid(currentData.getUid()); - model.setSentMobile(sent); - model.setReceivedMobile(received); - model.setSession(session); - model.setType(type); - - Long total = sent + received; - Long deviceTotal = getDeviceMobileDataUsage(mContext, session, date)[2]; - - // multiplied by 2 just to increase progress a bit. - Double progress = ((total.doubleValue() / deviceTotal.doubleValue()) * 100) * 2; - int progressInt; - if (progress != null) { - progressInt = progress.intValue(); - } else { - progressInt = 0; - } - model.setProgress(progressInt); - - mSystemAppsList.add(model); - } - - } catch (ParseException e) { - e.printStackTrace(); - } catch (RemoteException e) { - e.printStackTrace(); - } - } else { - try { - sent = getAppWifiDataUsage(mContext, currentData.getUid(), session)[0]; - received = getAppWifiDataUsage(mContext, currentData.getUid(), session)[1]; - totalSystemSent = totalSystemSent + sent; - totalSystemReceived = totalSystemReceived + received; - - if (sent > 0 || received > 0) { - model = new AppDataUsageModel(); - model.setAppName(currentData.getAppName()); - model.setPackageName(currentData.getPackageName()); - model.setUid(currentData.getUid()); - model.setSentMobile(sent); - model.setReceivedMobile(received); - model.setSession(session); - model.setType(type); - - Long total = sent + received; - Long deviceTotal = getDeviceWifiDataUsage(mContext, session)[2]; - - Double progress = ((total.doubleValue() / deviceTotal.doubleValue()) * 100) * 2; - int progressInt; - if (progress != null) { - progressInt = progress.intValue(); - } else { - progressInt = 0; - } - model.setProgress(progressInt); - - mSystemAppsList.add(model); - } - } catch (ParseException e) { - e.printStackTrace(); - } catch (RemoteException e) { - e.printStackTrace(); - } - } - } else { - if (isAppInstalled(mContext, currentData.getPackageName())) { - if (type == TYPE_MOBILE_DATA) { - try { - sent = getAppMobileDataUsage(mContext, currentData.getUid(), session)[0]; - received = getAppMobileDataUsage(mContext, currentData.getUid(), session)[1]; - - if (sent > 0 || received > 0) { - model = new AppDataUsageModel(); - model.setAppName(currentData.getAppName()); - model.setPackageName(currentData.getPackageName()); - model.setUid(currentData.getUid()); - model.setSentMobile(sent); - model.setReceivedMobile(received); - model.setSession(session); - model.setType(type); - - Long total = sent + received; - Long deviceTotal = getDeviceMobileDataUsage(mContext, session, date)[2]; - - Double progress = ((total.doubleValue() / deviceTotal.doubleValue()) * 100) * 2; - int progressInt; - if (progress != null) { - progressInt = progress.intValue(); - } else { - progressInt = 0; - } - model.setProgress(progressInt); - - mUserAppsList.add(model); - } - - - } catch (ParseException e) { - e.printStackTrace(); - } catch (RemoteException e) { - e.printStackTrace(); - } - } else { - try { - sent = getAppWifiDataUsage(mContext, currentData.getUid(), session)[0]; - received = getAppWifiDataUsage(mContext, currentData.getUid(), session)[1]; - - if (sent > 0 || received > 0) { - model = new AppDataUsageModel(); - model.setAppName(currentData.getAppName()); - model.setPackageName(currentData.getPackageName()); - model.setUid(currentData.getUid()); - model.setSentMobile(sent); - model.setReceivedMobile(received); - model.setSession(session); - model.setType(type); - - Long total = sent + received; - Long deviceTotal = getDeviceWifiDataUsage(mContext, session)[2]; - - Double progress = ((total.doubleValue() / deviceTotal.doubleValue()) * 100) * 2; - int progressInt; - if (progress != null) { - progressInt = progress.intValue(); - } else { - progressInt = 0; - } - model.setProgress(progressInt); - - mUserAppsList.add(model); - } - - } catch (ParseException e) { - e.printStackTrace(); - } catch (RemoteException e) { - e.printStackTrace(); - } - } - } - } - } - - model = new AppDataUsageModel(); - model.setAppName(mContext.getString(R.string.label_system_apps)); - model.setPackageName(mContext.getString(R.string.package_system)); - model.setSentMobile(totalSystemSent); - model.setReceivedMobile(totalSystemReceived); - model.setSession(session); - model.setType(type); - - Long total = totalSystemSent + totalSystemReceived; - - Long deviceTotal = null; - if (type == TYPE_MOBILE_DATA) { - try { - deviceTotal = getDeviceMobileDataUsage(mContext, session, date)[2]; - Double progress = ((total.doubleValue() / deviceTotal.doubleValue()) * 100) * 2; - int progressInt; - if (progress != null) { - progressInt = progress.intValue(); - } else { - progressInt = 0; - } - model.setProgress(progressInt); - - } catch (ParseException e) { - e.printStackTrace(); - } catch (RemoteException e) { - e.printStackTrace(); - } - } else { - try { - deviceTotal = getDeviceWifiDataUsage(mContext, session)[2]; - Double progress = ((total.doubleValue() / deviceTotal.doubleValue()) * 100) * 2; - int progressInt; - if (progress != null) { - progressInt = progress.intValue(); - } else { - progressInt = 0; - } - model.setProgress(progressInt); - - } catch (ParseException e) { - e.printStackTrace(); - } catch (RemoteException e) { - e.printStackTrace(); - } - } - - if (deviceTotal > 0) { - mUserAppsList.add(model); - } - - try { - if (type == TYPE_MOBILE_DATA) { - totalTetheringSent = getTetheringDataUsage(mContext, session)[0]; - totalTetheringReceived = getTetheringDataUsage(mContext, session)[1]; - tetheringTotal = totalTetheringSent + totalTetheringReceived; - - Double tetheringProgress = ((tetheringTotal.doubleValue() / deviceTotal.doubleValue()) * 100) * 2; - int tetheringProgressInt; - if (tetheringProgress != null) { - tetheringProgressInt = tetheringProgress.intValue(); - } else { - tetheringProgressInt = 0; - } - - model = new AppDataUsageModel(); - model.setAppName(mContext.getString(R.string.label_tethering)); - model.setPackageName(mContext.getString(R.string.package_tethering)); - model.setSentMobile(totalTetheringSent); - model.setReceivedMobile(totalTetheringReceived); - model.setSession(session); - model.setType(type); - model.setProgress(tetheringProgressInt); - - if (tetheringTotal > 0) { - mUserAppsList.add(model); - } - - - totalDeletedAppsSent = getDeletedAppsMobileDataUsage(mContext, session)[0]; - totalDeletedAppsReceived = getDeletedAppsMobileDataUsage(mContext, session)[1]; - } else { - totalDeletedAppsSent = getDeletedAppsWifiDataUsage(mContext, session)[0]; - totalDeletedAppsReceived = getDeletedAppsWifiDataUsage(mContext, session)[1]; - } - deletedAppsTotal = totalDeletedAppsSent + totalDeletedAppsReceived; - - Double deletedProgress = ((deletedAppsTotal.doubleValue() / deviceTotal.doubleValue()) * 100) * 2; - int deletedProgressInt; - if (deletedProgress != null) { - deletedProgressInt = deletedProgress.intValue(); - } else { - deletedProgressInt = 0; - } - - model = new AppDataUsageModel(); - model.setAppName(mContext.getString(R.string.label_removed)); - model.setPackageName(mContext.getString(R.string.package_removed)); - model.setSentMobile(totalDeletedAppsSent); - model.setReceivedMobile(totalDeletedAppsReceived); - model.setSession(session); - model.setType(type); - model.setProgress(deletedProgressInt); - - if (deletedAppsTotal > 0) { - mUserAppsList.add(model); - } - - Collections.sort(mUserAppsList, new Comparator() { - @Override - public int compare(AppDataUsageModel o1, AppDataUsageModel o2) { - o1.setMobileTotal((o1.getSentMobile() + o1.getReceivedMobile()) / 1024f); - o2.setMobileTotal((o2.getSentMobile() + o2.getReceivedMobile()) / 1024f); - return o1.getMobileTotal().compareTo(o2.getMobileTotal()); - } - }); - - Collections.reverse(mUserAppsList); - - Collections.sort(mSystemAppsList, new Comparator() { - @Override - public int compare(AppDataUsageModel o1, AppDataUsageModel o2) { - o1.setMobileTotal((o1.getSentMobile() + o1.getReceivedMobile()) / 1024f); - o2.setMobileTotal((o2.getSentMobile() + o2.getReceivedMobile()) / 1024f); - return o1.getMobileTotal().compareTo(o2.getMobileTotal()); - } - }); - - Collections.reverse(mSystemAppsList); - - - } catch (ParseException e) { - e.printStackTrace(); - } catch (RemoteException e) { - e.printStackTrace(); - } - - return null; - } - - @Override - protected void onPostExecute(Object o) { - super.onPostExecute(o); - isDataLoading = false; - if (getAppContext() != null) { - onDataLoaded(getAppContext()); - } else { - - } - MainActivity.FetchApps fetchApps = new MainActivity.FetchApps(mContext); - fetchApps.execute(); - } - - } - - private void verifyAppVersion() { - String updateVersion = SharedPreferences.getAppPrefs(MainActivity.this) - .getString(UPDATE_VERSION, BuildConfig.VERSION_NAME); - if (updateVersion.equalsIgnoreCase(BuildConfig.VERSION_NAME)) { - SharedPreferences.getAppPrefs(MainActivity.this) - .edit().remove(UPDATE_VERSION).apply(); - } - } - - public static void setTheme(Activity activity) { - String theme = PreferenceManager.getDefaultSharedPreferences(activity).getString(APP_THEME, "system"); - switch (theme) { - case "dark": - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); - break; - - case "light": - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); - break; - - case "system": - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); - break; - - default: - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); - break; - } - - } -} \ No newline at end of file diff --git a/app/src/main/java/com/drnoob/datamonitor/ui/activities/MainActivity.kt b/app/src/main/java/com/drnoob/datamonitor/ui/activities/MainActivity.kt new file mode 100644 index 00000000..a9a6402f --- /dev/null +++ b/app/src/main/java/com/drnoob/datamonitor/ui/activities/MainActivity.kt @@ -0,0 +1,448 @@ +/* + * Copyright (C) 2021 Dr.NooB + * + * This file is a part of Data Monitor + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.drnoob.datamonitor.ui.activities + +import android.Manifest +import android.app.AlarmManager +import android.app.NotificationChannel +import android.app.NotificationManager +import android.appwidget.AppWidgetManager +import android.content.ComponentName +import android.content.ContentResolver +import android.content.Intent +import android.content.pm.PackageManager +import android.media.AudioAttributes +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.os.PowerManager +import android.provider.Settings +import android.util.Log +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.widget.RemoteViews +import androidx.activity.OnBackPressedCallback +import androidx.activity.viewModels +import androidx.annotation.RequiresApi +import androidx.appcompat.app.AppCompatActivity +import androidx.navigation.NavDestination +import androidx.navigation.fragment.NavHostFragment +import androidx.navigation.ui.NavigationUI.setupWithNavController +import androidx.preference.PreferenceManager +import com.drnoob.datamonitor.BuildConfig +import com.drnoob.datamonitor.Common +import com.drnoob.datamonitor.R +import com.drnoob.datamonitor.Widget.DataUsageWidget +import com.drnoob.datamonitor.adapters.data.DataUsageViewModel +import com.drnoob.datamonitor.adapters.data.DataUsageViewModelFactory +import com.drnoob.datamonitor.core.Values +import com.drnoob.datamonitor.core.task.DatabaseHandler +import com.drnoob.datamonitor.databinding.ActivityMainBinding +import com.drnoob.datamonitor.utils.CrashReporter +import com.drnoob.datamonitor.utils.SharedPreferences +import com.drnoob.datamonitor.utils.helpers.UsageDataHelperImpl +import com.drnoob.datamonitor.utils.helpers.setTheme +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.elevation.SurfaceColors + +class MainActivity : AppCompatActivity() { + + lateinit var binding: ActivityMainBinding + private val viewModel: DataUsageViewModel by viewModels { + DataUsageViewModelFactory(UsageDataHelperImpl(this)) + } + + private val onBackPressedCallback: OnBackPressedCallback = + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + val navHostFragment = supportFragmentManager.findFragmentById(R.id.main_nav_host_fragment) as NavHostFragment + val navController = navHostFragment.navController + if (navController.currentDestination?.id == R.id.bottom_menu_home) { + finish() + } else { + binding.bottomNavigationView.selectedItemId = R.id.bottom_menu_home + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + setTheme(this) + Thread.setDefaultUncaughtExceptionHandler(CrashReporter(this@MainActivity)) + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + if (!Common.isReadPhoneStateGranted(this@MainActivity)) { + startActivity( + Intent(this, SetupActivity::class.java) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + .putExtra(Values.SETUP_VALUE, Values.READ_PHONE_STATE_DISABLED) + ) + finish() + } + } + super.onCreate(savedInstanceState) + val languageCode = + SharedPreferences.getUserPrefs(this).getString(Values.APP_LANGUAGE_CODE, "null") + val countryCode = + SharedPreferences.getUserPrefs(this).getString(Values.APP_COUNTRY_CODE, "") + if (languageCode == "null") { + Common.setLanguage(this, "en", countryCode) + } else { + Common.setLanguage(this, languageCode, countryCode) + } + try { + Common.refreshService(this) + } catch (e: Exception) { + e.printStackTrace() + } + try { + if (Common.isUsageAccessGranted(this@MainActivity)) { + binding = ActivityMainBinding.inflate( + layoutInflater + ) + setTheme(R.style.Theme_DataMonitor) + setContentView(binding.root) + setSupportActionBar(binding.mainToolbar) + binding.mainToolbar.setBackgroundColor(SurfaceColors.SURFACE_2.getColor(this)) + window.statusBarColor = SurfaceColors.SURFACE_2.getColor(this) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { + window.navigationBarColor = SurfaceColors.SURFACE_2.getColor(this) + } + SharedPreferences.getUserPrefs(this).edit().putBoolean(Values.SETUP_COMPLETED, true) + .apply() + if (binding.bottomNavigationView.selectedItemId == R.id.bottom_menu_home) { + supportActionBar!!.title = getString(R.string.app_name) + } + val navHostFragment = + supportFragmentManager.findFragmentById(R.id.main_nav_host_fragment) as NavHostFragment? + val controller = navHostFragment!!.navController + controller.addOnDestinationChangedListener { _, navDestination, _ -> + changeBanner( + navDestination + ) + } // working + setupWithNavController(binding.bottomNavigationView, controller) + val databaseHandler = DatabaseHandler(this@MainActivity) + if (databaseHandler.usageList == null || databaseHandler.usageList.size <= 0) { + viewModel.fetchApps() + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createNotificationChannel() + } + value = intent.getIntExtra(Values.DATA_USAGE_VALUE, 0) + if (value == Values.DATA_USAGE_SYSTEM) { + binding.bottomNavigationView.visibility = View.GONE + binding.bottomNavigationView.selectedItemId = R.id.bottom_menu_app_data_usage + supportActionBar!!.setTitle(R.string.system_data_usage) + supportActionBar!!.setDisplayHomeAsUpEnabled(true) + supportActionBar!!.setHomeAsUpIndicator(R.drawable.ic_arrow) + controller.navigate(R.id.system_data_usage) // >> working + } +// else if (!viewModel.isDataLoading) { +// viewModel.isDataLoading = true +// viewModel.loadData(Values.SESSION_TODAY, Values.TYPE_MOBILE_DATA) +// viewModel.isDataLoading = false +// viewModel.fetchApps() +// if (AppDataUsageFragment.appContext != null) { +// viewModel.callOnDataLoaded.value = true +// } +// } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + val notificationManager = getSystemService( + NotificationManager::class.java + ) + if (!notificationManager.areNotificationsEnabled()) { + requestPermissions( + arrayOf(Manifest.permission.POST_NOTIFICATIONS), + Values.REQUEST_POST_NOTIFICATIONS + ) + } + } + } else { + onResume() + } + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + } catch (e: Exception) { + e.printStackTrace() + } + + this@MainActivity.onBackPressedDispatcher + .addCallback(this, onBackPressedCallback) + } + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + if (requestCode == Values.REQUEST_POST_NOTIFICATIONS) { + if (grantResults.isNotEmpty() && grantResults[0] != PackageManager.PERMISSION_GRANTED) { + MaterialAlertDialogBuilder(this@MainActivity) + .setTitle(R.string.label_permission_denied) + .setMessage(R.string.notification_permission_denied_body) + .setPositiveButton(R.string.action_grant) { _, _ -> + val intent = Intent() + intent.action = "android.settings.APP_NOTIFICATION_SETTINGS" + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + intent.putExtra("app_package", packageName) + intent.putExtra("app_uid", applicationInfo.uid) + intent.putExtra("android.provider.extra.APP_PACKAGE", packageName) + startActivity(intent) + } + .setNegativeButton(R.string.action_cancel, null) + .show() + } + } + } + + private fun checkBatteryOptimisationState() { + val powerManager = getSystemService(POWER_SERVICE) as PowerManager + if (powerManager.isIgnoringBatteryOptimizations(packageName)) { + // Battery optimisation is disabled + Log.d(TAG, "checkBatteryOptimisationState: Disabled") + } else { + // Battery optimisation is enabled + Log.d(TAG, "checkBatteryOptimisationState: Enabled") + if (SharedPreferences.getUserPrefs(this) + .getBoolean(Values.SHOULD_SHOW_BATTERY_OPTIMISATION_ERROR, true) + ) { + MaterialAlertDialogBuilder(this) + .setTitle(getString(R.string.label_battery_optimisation)) + .setMessage(getString(R.string.battery_optimisation_enabled_info)) + .setPositiveButton(getString(R.string.disable_battery_optimisation)) { dialog, _ -> + dialog.dismiss() + startActivity( + Intent(this@MainActivity, ContainerActivity::class.java) + .putExtra( + Values.GENERAL_FRAGMENT_ID, + Values.DISABLE_BATTERY_OPTIMISATION_FRAGMENT + ) + ) + } + .setNegativeButton(getString(R.string.label_do_not_show_again)) { dialog, _ -> + SharedPreferences.getUserPrefs(this@MainActivity).edit() + .putBoolean(Values.SHOULD_SHOW_BATTERY_OPTIMISATION_ERROR, false) + .apply() + dialog.dismiss() + } + .setNeutralButton(getString(R.string.action_cancel)) { dialog, _ -> dialog.dismiss() } + .show() + } + } + } + + private fun initializeBottomNavigationViewBar() { + val navHostFragment = + supportFragmentManager.findFragmentById(R.id.main_nav_host_fragment) as NavHostFragment? + val controller = navHostFragment!!.navController + controller.addOnDestinationChangedListener { _, navDestination, _ -> + changeBanner( + navDestination + ) + } // working + +// NavigationUI.setupWithNavController(binding.bottomNavigationView, controller); // working + } + + private fun changeBanner(navDestination: NavDestination) { + val destination = navDestination.label.toString() + if (destination.equals(getString(R.string.home), ignoreCase = true)) { + // Home Fragment + supportActionBar!!.title = getString(R.string.app_name) + } else if (destination.equals(getString(R.string.setup), ignoreCase = true)) { + // Setup Fragment + supportActionBar!!.title = getString(R.string.setup) + } else if (destination.equals(getString(R.string.app_data_usage), ignoreCase = true)) { + // App data usage Fragment + supportActionBar!!.title = getString(R.string.app_data_usage) + } else if (destination.equals(getString(R.string.network_diagnostics), ignoreCase = true)) { + // Network diagnostics Fragment + supportActionBar!!.title = getString(R.string.network_diagnostics) + } else { + // Unknown Fragment + } + } + + override fun onStart() { + super.onStart() + verifyAppVersion() + // initializeBottomNavigationViewBar(); + if (!PreferenceManager.getDefaultSharedPreferences(this@MainActivity) + .getBoolean(Values.ALARM_PERMISSION_DENIED, false) + ) { + val alarmManager = getSystemService(ALARM_SERVICE) as AlarmManager + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + if (!alarmManager.canScheduleExactAlarms()) { + MaterialAlertDialogBuilder(this) + .setTitle(getString(R.string.error_alarm_permission_denied)) + .setMessage(getString(R.string.error_alarm_permission_denied_dialog_summary)) + .setCancelable(false) + .setPositiveButton(getString(R.string.action_grant)) { dialog, _ -> + val intent = Intent() + intent.action = Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + dialog.dismiss() + startActivity(intent) + } + .setNegativeButton(getString(R.string.action_cancel)) { dialog, _ -> dialog.dismiss() } + .setOnDismissListener { + PreferenceManager.getDefaultSharedPreferences(this@MainActivity).edit() + .putBoolean(Values.ALARM_PERMISSION_DENIED, true) + .apply() + } + .show() + } + } + } + } + + override fun onResume() { + super.onResume() + try { + if (!Common.isUsageAccessGranted(this@MainActivity)) { + startActivity( + Intent(this, SetupActivity::class.java) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + .putExtra(Values.SETUP_VALUE, Values.USAGE_ACCESS_DISABLED) + ) + } + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + if (!Common.isReadPhoneStateGranted(this@MainActivity)) { + startActivity( + Intent(this, SetupActivity::class.java) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + .putExtra(Values.SETUP_VALUE, Values.READ_PHONE_STATE_DISABLED) + ) + } + } + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + } + try { + checkBatteryOptimisationState() + } catch (e: Exception) { + e.printStackTrace() + } + } + + override fun onDestroy() { + val intent = Intent(this@MainActivity, DataUsageWidget::class.java) + intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE + val ids = AppWidgetManager.getInstance(this@MainActivity) + .getAppWidgetIds(ComponentName(this@MainActivity, DataUsageWidget::class.java)) + AppWidgetManager.getInstance(this) + .updateAppWidget(ids, RemoteViews(packageName, R.layout.data_usage_widget)) + super.onDestroy() + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.toolbar_menu, menu) + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + android.R.id.home -> if (value == Values.DATA_USAGE_SYSTEM) { + value = 0 + finish() + } + + R.id.toolbar_settings -> startActivity( + Intent(this@MainActivity, ContainerActivity::class.java) + .putExtra(Values.GENERAL_FRAGMENT_ID, Values.BOTTOM_NAVBAR_ITEM_SETTINGS) + ) + } + return super.onOptionsItemSelected(item) + } + + @RequiresApi(api = Build.VERSION_CODES.O) + private fun createNotificationChannel() { + val usageChannel = NotificationChannel( + Values.DATA_USAGE_NOTIFICATION_CHANNEL_ID, + Values.DATA_USAGE_NOTIFICATION_CHANNEL_NAME, + NotificationManager.IMPORTANCE_LOW + ) + val warningChannel = NotificationChannel( + Values.DATA_USAGE_WARNING_CHANNEL_ID, Values.DATA_USAGE_WARNING_CHANNEL_NAME, + NotificationManager.IMPORTANCE_HIGH + ) + val appWarningChannel = NotificationChannel( + Values.APP_DATA_USAGE_WARNING_CHANNEL_ID, Values.APP_DATA_USAGE_WARNING_CHANNEL_NAME, + NotificationManager.IMPORTANCE_HIGH + ) + val networkSignalChannel = NotificationChannel( + Values.NETWORK_SIGNAL_CHANNEL_ID, Values.NETWORK_SIGNAL_CHANNEL_NAME, + NotificationManager.IMPORTANCE_HIGH + ) + val otherChannel = NotificationChannel( + Values.OTHER_NOTIFICATION_CHANNEL_ID, Values.OTHER_NOTIFICATION_CHANNEL_NAME, + NotificationManager.IMPORTANCE_HIGH + ) + warningChannel.enableVibration(true) + warningChannel.enableLights(true) + appWarningChannel.enableVibration(true) + appWarningChannel.enableLights(true) + val sound = + Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + packageName + "/" + R.raw.silent) + val attributes = AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_NOTIFICATION) + .build() + // networkSignalChannel.setSound(sound, attributes); + networkSignalChannel.setSound(Uri.EMPTY, null) + networkSignalChannel.setShowBadge(false) + networkSignalChannel.enableVibration(false) + networkSignalChannel.enableLights(false) + networkSignalChannel.setBypassDnd(true) + otherChannel.enableVibration(true) + otherChannel.enableLights(true) + val channels: MutableList = ArrayList() + channels.add(usageChannel) + channels.add(warningChannel) + channels.add(appWarningChannel) + channels.add(networkSignalChannel) + channels.add(otherChannel) + val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager + if (PreferenceManager.getDefaultSharedPreferences(this@MainActivity) + .getBoolean(Values.UPDATE_NOTIFICATION_CHANNEL, true) + ) { + notificationManager.deleteNotificationChannel("NetworkSignal.Notification") + PreferenceManager.getDefaultSharedPreferences(this@MainActivity).edit() + .putBoolean(Values.UPDATE_NOTIFICATION_CHANNEL, false) + .apply() + } + notificationManager.createNotificationChannels(channels) + } + + private fun verifyAppVersion() { + val updateVersion = SharedPreferences.getAppPrefs(this@MainActivity) + .getString(Values.UPDATE_VERSION, BuildConfig.VERSION_NAME) + if (updateVersion.equals(BuildConfig.VERSION_NAME, ignoreCase = true)) { + SharedPreferences.getAppPrefs(this@MainActivity) + .edit().remove(Values.UPDATE_VERSION).apply() + } + } + + + companion object { + private val TAG = MainActivity::class.java.simpleName + var value = 0 + var refreshAppDataUsage = false + } +} \ No newline at end of file diff --git a/app/src/main/java/com/drnoob/datamonitor/ui/fragments/AppDataUsageFragment.java b/app/src/main/java/com/drnoob/datamonitor/ui/fragments/AppDataUsageFragment.java deleted file mode 100644 index 5a49b85d..00000000 --- a/app/src/main/java/com/drnoob/datamonitor/ui/fragments/AppDataUsageFragment.java +++ /dev/null @@ -1,668 +0,0 @@ -/* - * Copyright (C) 2021 Dr.NooB - * - * This file is a part of Data Monitor - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.drnoob.datamonitor.ui.fragments; - -import static com.drnoob.datamonitor.core.Values.DAILY_DATA_HOME_ACTION; -import static com.drnoob.datamonitor.core.Values.DATA_RESET; -import static com.drnoob.datamonitor.core.Values.DATA_RESET_CUSTOM; -import static com.drnoob.datamonitor.core.Values.DATA_RESET_DATE; -import static com.drnoob.datamonitor.core.Values.DATA_USAGE_SESSION; -import static com.drnoob.datamonitor.core.Values.DATA_USAGE_TYPE; -import static com.drnoob.datamonitor.core.Values.EXTRA_IS_WEEK_DAY_VIEW; -import static com.drnoob.datamonitor.core.Values.EXTRA_WEEK_DAY; -import static com.drnoob.datamonitor.core.Values.SESSION_ALL_TIME; -import static com.drnoob.datamonitor.core.Values.SESSION_CUSTOM; -import static com.drnoob.datamonitor.core.Values.SESSION_LAST_MONTH; -import static com.drnoob.datamonitor.core.Values.SESSION_THIS_MONTH; -import static com.drnoob.datamonitor.core.Values.SESSION_THIS_YEAR; -import static com.drnoob.datamonitor.core.Values.SESSION_TODAY; -import static com.drnoob.datamonitor.core.Values.SESSION_YESTERDAY; -import static com.drnoob.datamonitor.core.Values.TYPE_MOBILE_DATA; -import static com.drnoob.datamonitor.core.Values.TYPE_WIFI; -import static com.drnoob.datamonitor.ui.activities.MainActivity.getRefreshAppDataUsage; -import static com.drnoob.datamonitor.ui.activities.MainActivity.isDataLoading; -import static com.drnoob.datamonitor.ui.activities.MainActivity.mSystemAppsList; -import static com.drnoob.datamonitor.ui.activities.MainActivity.mUserAppsList; -import static com.drnoob.datamonitor.ui.activities.MainActivity.setRefreshAppDataUsage; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.Context; -import android.content.DialogInterface; -import android.os.Bundle; -import android.os.RemoteException; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.widget.LinearLayout; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.constraintlayout.widget.ConstraintLayout; -import androidx.fragment.app.Fragment; -import androidx.lifecycle.ViewModelProvider; -import androidx.preference.PreferenceManager; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; - -import com.drnoob.datamonitor.R; -import com.drnoob.datamonitor.adapters.AppDataUsageAdapter; -import com.drnoob.datamonitor.adapters.data.AppDataUsageModel; -import com.drnoob.datamonitor.adapters.data.FragmentViewModel; -import com.drnoob.datamonitor.ui.activities.MainActivity; -import com.drnoob.datamonitor.utils.NetworkStatsHelper; -import com.drnoob.datamonitor.utils.VibrationUtils; -import com.google.android.material.bottomsheet.BottomSheetBehavior; -import com.google.android.material.bottomsheet.BottomSheetDialog; -import com.google.android.material.chip.Chip; -import com.google.android.material.chip.ChipGroup; -import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton; - -import java.text.ParseException; -import java.util.ArrayList; -import java.util.List; - -public class AppDataUsageFragment extends Fragment { - private static final String TAG = AppDataUsageFragment.class.getSimpleName(); - public static RecyclerView mAppsView; - public static AppDataUsageAdapter mAdapter; - public static List mList = new ArrayList<>(); - public static List mSystemList = new ArrayList<>(); - private static LinearLayout mLoading; - private static Context mContext; - private static Activity mActivity; - private static SwipeRefreshLayout mDataRefresh; - private static TextView mEmptyList; - private FragmentViewModel viewModel; - private ExtendedFloatingActionButton mFilter; - private static TextView mTotalUsage; - private static boolean fromHome; - private static boolean isWeekDayView; - private static String totalDataUsage; - private static int selectedSession, selectedType; - - public AppDataUsageFragment() { - - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - - @Override - public void onAttach(@NonNull Context context) { - super.onAttach(context); - mContext = context; - mActivity = getActivity(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_app_data_usage, container, false); - - viewModel = new ViewModelProvider(getActivity()).get(FragmentViewModel.class); - - mAppsView = view.findViewById(R.id.app_data_usage_recycler); - mLoading = view.findViewById(R.id.layout_list_loading); - mDataRefresh = view.findViewById(R.id.refresh_data_usage); - mEmptyList = view.findViewById(R.id.empty_list); - mTotalUsage = view.findViewById(R.id.current_session_total); - mFilter = view.findViewById(R.id.filter_app_usage); - - mAdapter = new AppDataUsageAdapter(mList, mContext); - mAdapter.setActivity(getActivity()); - - int session = getActivity().getIntent().getIntExtra(DATA_USAGE_SESSION, SESSION_TODAY); - int type = getActivity().getIntent().getIntExtra(DATA_USAGE_TYPE, TYPE_MOBILE_DATA); - fromHome = getActivity().getIntent().getBooleanExtra(DAILY_DATA_HOME_ACTION, false); - isWeekDayView = getActivity().getIntent().getBooleanExtra(EXTRA_IS_WEEK_DAY_VIEW, false); - - if (getActivity().getIntent() != null) { - if (fromHome) { - type = getActivity().getIntent().getIntExtra(DATA_USAGE_TYPE, TYPE_MOBILE_DATA); - setType(type); - refreshData(); -// mTopBar.setVisibility(View.GONE); - mFilter.setVisibility(View.GONE); - mAppsView.setPadding(0, 130, 0, 0); - } - else if (isWeekDayView) { - String weekDay = getActivity().getIntent().getStringExtra(EXTRA_WEEK_DAY); - } - } - - setSession(session); - setType(type); - mTotalUsage.setText("..."); - - Log.e(TAG, "onCreateView: " + getRefreshAppDataUsage() ); - if (getRefreshAppDataUsage()) { - refreshData(); - } - - mList = mUserAppsList; - mSystemList = mSystemAppsList; - - if (!MainActivity.isDataLoading()) { - mLoading.setAlpha(0.0f); - mAppsView.setAlpha(1.0f); - onDataLoaded(getContext()); - } - else { - mDataRefresh.setRefreshing(true); - } - - mFilter.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (isDataLoading()) { - return; - } - BottomSheetDialog dialog = new BottomSheetDialog(getContext(), R.style.BottomSheet); - View dialogView = LayoutInflater.from(getContext()).inflate(R.layout.layout_app_usage_filter, null); - - ChipGroup sessionGroup = dialogView.findViewById(R.id.session_group); - ChipGroup typeGroup = dialogView.findViewById(R.id.type_group); - - ConstraintLayout footer = dialogView.findViewById(R.id.footer); - TextView cancel = footer.findViewById(R.id.cancel); - TextView ok = footer.findViewById(R.id.ok); - - Chip sessionCurrentPlan = sessionGroup.findViewById(R.id.session_current_plan); - - if (PreferenceManager.getDefaultSharedPreferences(getContext()) - .getString(DATA_RESET, "null") - .equals(DATA_RESET_CUSTOM)) { - sessionCurrentPlan.setVisibility(View.VISIBLE); - } - else { - sessionCurrentPlan.setVisibility(View.GONE); - } - - sessionGroup.setOnCheckedStateChangeListener(new ChipGroup.OnCheckedStateChangeListener() { - @Override - public void onCheckedChanged(@NonNull ChipGroup group, @NonNull List checkedIds) { - if (!PreferenceManager.getDefaultSharedPreferences(getContext()) - .getBoolean("disable_haptics", false)) { - VibrationUtils.hapticMinor(getContext()); - } - } - }); - - typeGroup.setOnCheckedStateChangeListener(new ChipGroup.OnCheckedStateChangeListener() { - @Override - public void onCheckedChanged(@NonNull ChipGroup group, @NonNull List checkedIds) { - if (!PreferenceManager.getDefaultSharedPreferences(getContext()) - .getBoolean("disable_haptics", false)) { - VibrationUtils.hapticMinor(getContext()); - } - } - }); - - switch (getSession()) { - case SESSION_TODAY: - sessionGroup.check(R.id.session_today); - break; - - case SESSION_YESTERDAY: - sessionGroup.check(R.id.session_yesterday); - break; - - case SESSION_THIS_MONTH: - sessionGroup.check(R.id.session_this_month); - break; - - case SESSION_LAST_MONTH: - sessionGroup.check(R.id.session_last_month); - break; - - case SESSION_THIS_YEAR: - sessionGroup.check(R.id.session_this_year); - break; - - case SESSION_ALL_TIME: - sessionGroup.check(R.id.session_all_time); - break; - - case SESSION_CUSTOM: - sessionGroup.check(R.id.session_current_plan); - break; - - } - - switch (getType()) { - case TYPE_MOBILE_DATA: - typeGroup.check(R.id.type_mobile); - break; - - case TYPE_WIFI: - typeGroup.check(R.id.type_wifi); - break; - } - - cancel.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - dialog.dismiss(); - } - }); - - ok.setOnClickListener(new View.OnClickListener() { - @SuppressLint("NonConstantResourceId") - @Override - public void onClick(View v) { - switch (sessionGroup.getCheckedChipId()) { - case R.id.session_yesterday: - selectedSession = SESSION_YESTERDAY; - break; - - case R.id.session_this_month: - selectedSession = SESSION_THIS_MONTH; - break; - - case R.id.session_last_month: - selectedSession = SESSION_LAST_MONTH; - break; - - case R.id.session_this_year: - selectedSession = SESSION_THIS_YEAR; - break; - - case R.id.session_all_time: - selectedSession = SESSION_ALL_TIME; - break; - - case R.id.session_current_plan: - selectedSession = SESSION_CUSTOM; - break; - case R.id.session_today: - - default: - selectedSession = SESSION_TODAY; - break; - } - - switch (typeGroup.getCheckedChipId()) { - case R.id.type_wifi: - selectedType = TYPE_WIFI; - break; - - case R.id.type_mobile: - - default: - selectedType = TYPE_MOBILE_DATA; - break; - } - - if (!MainActivity.isDataLoading()) { - refreshData(); - } - dialog.dismiss(); - } - }); - - dialog.setContentView(dialogView); - dialog.setOnShowListener(new DialogInterface.OnShowListener() { - @Override - public void onShow(DialogInterface dialogInterface) { - BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialogInterface; - FrameLayout bottomSheet = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet); - BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED); - } - }); - dialog.show(); - - } - }); - - mDataRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { - @Override - public void onRefresh() { - refreshData(); - } - }); - -// mSession.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// if (isDataLoading()) { -// return; -// } -// BottomSheetDialog dialog = new BottomSheetDialog(getContext(), R.style.BottomSheet); -// View dialogView = LayoutInflater.from(getContext()).inflate(R.layout.data_usage_session, null); -// -// RadioGroup sessions = dialogView.findViewById(R.id.session_group); -// ConstraintLayout footer = dialogView.findViewById(R.id.footer); -// TextView cancel = footer.findViewById(R.id.cancel); -// TextView ok = footer.findViewById(R.id.ok); -// -// switch (getSession(getContext())) { -// case SESSION_TODAY: -// sessions.check(R.id.session_today); -// break; -// -// case SESSION_YESTERDAY: -// sessions.check(R.id.session_yesterday); -// break; -// -// case SESSION_THIS_MONTH: -// sessions.check(R.id.session_this_month); -// break; -// -// case SESSION_LAST_MONTH: -// sessions.check(R.id.session_last_month); -// break; -// -// case SESSION_THIS_YEAR: -// sessions.check(R.id.session_this_year); -// break; -// -// case SESSION_ALL_TIME: -// sessions.check(R.id.session_all_time); -// break; -// -// } -// -// cancel.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// dialog.dismiss(); -// } -// }); -// -// ok.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// String session = null; -// switch (sessions.getCheckedRadioButtonId()) { -// case R.id.session_today: -// session = getString(R.string.label_today); -// break; -// -// case R.id.session_yesterday: -// session = getString(R.string.label_yesterday); -// break; -// -// case R.id.session_this_month: -// session = getString(R.string.label_this_month); -// break; -// -// case R.id.session_last_month: -// session = getString(R.string.label_last_month); -// break; -// -// case R.id.session_this_year: -// session = getString(R.string.label_this_year); -// break; -// -// case R.id.session_all_time: -// session = getString(R.string.label_all_time); -// break; -// } -// mSession.setText(session); -// if (!MainActivity.isDataLoading()) { -// refreshData(); -// } -// dialog.dismiss(); -// } -// }); -// -// dialog.setContentView(dialogView); -// dialog.setOnShowListener(new DialogInterface.OnShowListener() { -// @Override -// public void onShow(DialogInterface dialogInterface) { -// BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialogInterface; -// FrameLayout bottomSheet = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet); -// BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED); -// } -// }); -// dialog.show(); -// } -// }); -// -// mType.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// if (isDataLoading()) { -// return; -// } -// BottomSheetDialog dialog = new BottomSheetDialog(getContext(), R.style.BottomSheet); -// View dialogView = LayoutInflater.from(getContext()).inflate(R.layout.data_usage_type, null); -// -// RadioGroup types = dialogView.findViewById(R.id.type_group); -// ConstraintLayout footer = dialogView.findViewById(R.id.footer); -// TextView cancel = footer.findViewById(R.id.cancel); -// TextView ok = footer.findViewById(R.id.ok); -// -// switch (getType(getContext())) { -// case TYPE_MOBILE_DATA: -// types.check(R.id.type_mobile); -// break; -// -// case TYPE_WIFI: -// types.check(R.id.type_wifi); -// break; -// } -// -// cancel.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// dialog.dismiss(); -// } -// }); -// -// ok.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// String type = null; -// switch (types.getCheckedRadioButtonId()) { -// case R.id.type_mobile: -// type = getString(R.string.label_mobile_data); -// break; -// -// case R.id.type_wifi: -// type = getString(R.string.label_wifi); -// break; -// } -// mType.setText(type); -// -// if (!MainActivity.isDataLoading()) { -// refreshData(); -// } -// dialog.dismiss(); -// } -// }); -// -// dialog.setContentView(dialogView); -// dialog.setOnShowListener(new DialogInterface.OnShowListener() { -// @Override -// public void onShow(DialogInterface dialogInterface) { -// BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialogInterface; -// FrameLayout bottomSheet = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet); -// BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED); -// } -// }); -// dialog.show(); -// } -// }); - - /* - Shrink or expand the FAB according to user scroll - */ - mAppsView.setOnScrollChangeListener(new View.OnScrollChangeListener() { - @Override - public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { - if (oldScrollY < -15 && mFilter.isExtended()) { - mFilter.shrink(); - } - else if (oldScrollY > 15 && !mFilter.isExtended()) { - mFilter.extend(); - } - else if (mAppsView.computeVerticalScrollOffset() == 0 && !mFilter.isExtended()) { - mFilter.extend(); - } - } - }); - - - return view; - } - - @Override - public void onStart() { - super.onStart(); - if (mList.size() > 0) { - setSession(mList.get(0).getSession()); - setType(mList.get(0).getType()); - } - else { - if (viewModel.getCurrentSession().getValue() != null && - viewModel.getCurrentType().getValue() != null) { - setSession(viewModel.getCurrentSession().getValue()); - setType(viewModel.getCurrentType().getValue()); - } - - } - if (!PreferenceManager.getDefaultSharedPreferences(requireContext()) - .getString(DATA_RESET, "null") - .equals(DATA_RESET_CUSTOM)) { - if (getSession() == SESSION_CUSTOM) { - setSession(SESSION_TODAY); - refreshData(); - } - } - } - - @Override - public void onPause() { - viewModel.setCurrentSession(getSession()); - viewModel.setCurrentType(getType()); - super.onPause(); - } - - public static Context getAppContext() { - return mContext; - } - - private static void refreshData() { - mLoading.animate().alpha(1.0f); - mAppsView.animate().alpha(0.0f); - mEmptyList.animate().alpha(0.0f); - mDataRefresh.setRefreshing(true); - mAppsView.removeAllViews(); - mList.clear(); - mSystemList.clear(); - totalDataUsage = ""; - mTotalUsage.setText("..."); - - - MainActivity.LoadData loadData = new MainActivity.LoadData(mContext, getSession(), - getType()); - if (!isDataLoading()) { - loadData.execute(); - } - - } - - public static void onDataLoaded(Context context) { - try { - if (totalDataUsage == null || totalDataUsage.isEmpty()) { - totalDataUsage = getTotalDataUsage(context); - } - mTotalUsage.setText(context.getString(R.string.total_usage, totalDataUsage)); - - } - catch (ParseException | RemoteException e) { - e.printStackTrace(); - } - Log.d(TAG, "onDataLoaded: " + mSystemList.size() + " system"); - Log.d(TAG, "onDataLoaded: " + mList.size() + " user"); - mAdapter = new AppDataUsageAdapter(mList, mContext); - mAdapter.setActivity(mActivity); - mAdapter.setFromHome(fromHome); - mAppsView.setAdapter(mAdapter); - mAppsView.setLayoutManager(new LinearLayoutManager(mContext)); - mLoading.animate().alpha(0.0f); - mAppsView.animate().alpha(1.0f); - mDataRefresh.setRefreshing(false); - if (mList.size() <= 0) { - mEmptyList.animate().alpha(1.0f); - } - else { - setSession(mList.get(0).getSession()); - setType(mList.get(0).getType()); - } - if (!fromHome) { - setRefreshAppDataUsage(false); - } - } - - private static String getTotalDataUsage(Context context) throws ParseException, RemoteException { - int date = PreferenceManager.getDefaultSharedPreferences(context).getInt(DATA_RESET_DATE, -1); - String totalUsage; - int type = getType(); - if (type == TYPE_MOBILE_DATA) { - totalUsage = NetworkStatsHelper.formatData( - NetworkStatsHelper.getTotalAppMobileDataUsage(context, getSession(), date)[0], - NetworkStatsHelper.getTotalAppMobileDataUsage(context, getSession(), date)[1] - )[2]; - } - else if (type == TYPE_WIFI) { - totalUsage = NetworkStatsHelper.formatData( - NetworkStatsHelper.getTotalAppWifiDataUsage(context, getSession())[0], - NetworkStatsHelper.getTotalAppWifiDataUsage(context, getSession())[1] - )[2]; - } - else { - totalUsage = context.getString(R.string.label_unknown); - } - return totalUsage; - } - - public static int getSession() { - if (selectedSession == 0) { - selectedSession = SESSION_TODAY; - } - - return selectedSession; - } - - public static int getType() { - if (selectedType == 0) { - selectedType = TYPE_MOBILE_DATA; - } - return selectedType; - } - - private static void setSession(int session) { - selectedSession = session; - } - - private static void setType(int type) { - selectedType = type; - } - -} diff --git a/app/src/main/java/com/drnoob/datamonitor/ui/fragments/AppDataUsageFragment.kt b/app/src/main/java/com/drnoob/datamonitor/ui/fragments/AppDataUsageFragment.kt new file mode 100644 index 00000000..4b83d75b --- /dev/null +++ b/app/src/main/java/com/drnoob/datamonitor/ui/fragments/AppDataUsageFragment.kt @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2021 Dr.NooB + * + * This file is a part of Data Monitor + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.drnoob.datamonitor.ui.fragments + +import android.annotation.SuppressLint +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import android.os.Bundle +import android.os.RemoteException +import android.provider.Settings +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageView +import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.lifecycleScope +import androidx.preference.PreferenceManager +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.drnoob.datamonitor.Common +import com.drnoob.datamonitor.R +import com.drnoob.datamonitor.adapters.UsageDataAdapter +import com.drnoob.datamonitor.adapters.data.AppDataUsageModel +import com.drnoob.datamonitor.adapters.data.DataUsageViewModel +import com.drnoob.datamonitor.adapters.data.DataUsageViewModelFactory +import com.drnoob.datamonitor.core.Values +import com.drnoob.datamonitor.databinding.FragmentAppDataUsageBinding +import com.drnoob.datamonitor.ui.activities.ContainerActivity +import com.drnoob.datamonitor.utils.NetworkStatsHelper +import com.drnoob.datamonitor.utils.VibrationUtils +import com.drnoob.datamonitor.utils.helpers.UsageDataHelperImpl +import com.drnoob.datamonitor.utils.loadScreenTime +import com.drnoob.datamonitor.utils.setBoldSpan +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.google.android.material.button.MaterialButton +import com.google.android.material.chip.Chip +import com.google.android.material.chip.ChipGroup +import kotlinx.coroutines.launch +import java.text.ParseException + +class AppDataUsageFragment : Fragment() { + + private var _binding: FragmentAppDataUsageBinding? = null + private val binding get() = _binding!! + + private var session: Int = Values.SESSION_TODAY + private var type: Int = Values.TYPE_MOBILE_DATA + private var totalDataUsage: String? = null + private var fromHome: Boolean = false + + private val dataUsageViewModel: DataUsageViewModel by activityViewModels { + DataUsageViewModelFactory(UsageDataHelperImpl(requireActivity())) + } + + private lateinit var recyclerView: RecyclerView + private lateinit var adapter: UsageDataAdapter + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentAppDataUsageBinding + .inflate(inflater, container, false) + return binding.root + } + + @SuppressLint("InflateParams") + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + session = + requireActivity().intent.getIntExtra(Values.DATA_USAGE_SESSION, Values.SESSION_TODAY) + type = requireActivity().intent.getIntExtra(Values.DATA_USAGE_TYPE, Values.TYPE_MOBILE_DATA) + fromHome = requireActivity().intent.getBooleanExtra(Values.DAILY_DATA_HOME_ACTION, false) + if (fromHome) binding.filterAppUsage.visibility = View.GONE + + binding.currentSessionTotal.text = "..." + + refreshData() + recyclerView = binding.appDataUsageRecycler + recyclerView.layoutManager = LinearLayoutManager(context) + adapter = UsageDataAdapter(requireContext()) + recyclerView.adapter = adapter + + binding.filterAppUsage.setOnClickListener { + val dialog = BottomSheetDialog(requireContext(), R.style.BottomSheet) + val dialogView = + LayoutInflater.from(context).inflate(R.layout.layout_app_usage_filter, null) + val sessionGroup = dialogView.findViewById(R.id.session_group) + val typeGroup = dialogView.findViewById(R.id.type_group) + val footer = dialogView.findViewById(R.id.footer) + val cancel = footer.findViewById(R.id.cancel) + val ok = footer.findViewById(R.id.ok) + val sessionCurrentPlan = sessionGroup.findViewById(R.id.session_current_plan) + if (PreferenceManager.getDefaultSharedPreferences(requireContext()) + .getString(Values.DATA_RESET, "null") + == Values.DATA_RESET_CUSTOM + ) { + sessionCurrentPlan.visibility = View.VISIBLE + } else { + sessionCurrentPlan.visibility = View.GONE + } + sessionGroup.setOnCheckedStateChangeListener { _, _ -> + if (!PreferenceManager.getDefaultSharedPreferences(requireContext()) + .getBoolean("disable_haptics", false) + ) { + VibrationUtils.hapticMinor(context) + } + } + typeGroup.setOnCheckedStateChangeListener { _, _ -> + if (!PreferenceManager.getDefaultSharedPreferences(requireContext()) + .getBoolean("disable_haptics", false) + ) { + VibrationUtils.hapticMinor(context) + } + } + when (session) { + Values.SESSION_TODAY -> sessionGroup.check(R.id.session_today) + Values.SESSION_YESTERDAY -> sessionGroup.check(R.id.session_yesterday) + Values.SESSION_THIS_MONTH -> sessionGroup.check(R.id.session_this_month) + Values.SESSION_LAST_MONTH -> sessionGroup.check(R.id.session_last_month) + Values.SESSION_THIS_YEAR -> sessionGroup.check(R.id.session_this_year) + Values.SESSION_ALL_TIME -> sessionGroup.check(R.id.session_all_time) + Values.SESSION_CUSTOM -> sessionGroup.check(R.id.session_current_plan) + } + when (type) { + Values.TYPE_MOBILE_DATA -> typeGroup.check(R.id.type_mobile) + Values.TYPE_WIFI -> typeGroup.check(R.id.type_wifi) + } + cancel.setOnClickListener { dialog.dismiss() } + ok.setOnClickListener { + session = when (sessionGroup.checkedChipId) { + R.id.session_yesterday -> Values.SESSION_YESTERDAY + R.id.session_this_month -> Values.SESSION_THIS_MONTH + R.id.session_last_month -> Values.SESSION_LAST_MONTH + R.id.session_this_year -> Values.SESSION_THIS_YEAR + R.id.session_all_time -> Values.SESSION_ALL_TIME + R.id.session_current_plan -> Values.SESSION_CUSTOM + R.id.session_today -> Values.SESSION_TODAY + else -> Values.SESSION_TODAY + } + type = when (typeGroup.checkedChipId) { + R.id.type_wifi -> Values.TYPE_WIFI + R.id.type_mobile -> Values.TYPE_MOBILE_DATA + else -> Values.TYPE_MOBILE_DATA + } + refreshData() + dialog.dismiss() + } + dialog.setContentView(dialogView) + dialog.setOnShowListener { dialogInterface -> + val bottomSheetDialog = dialogInterface as BottomSheetDialog + val bottomSheet = + bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet) + bottomSheet?.let { + BottomSheetBehavior.from(it).state = BottomSheetBehavior.STATE_EXPANDED + } + } + dialog.show() + } + binding.refreshDataUsage.setOnRefreshListener { refreshData() } + + // Shrink or expand the FAB according to user scroll + binding.appDataUsageRecycler.setOnScrollChangeListener { _, _, _, _, oldScrollY -> + if (oldScrollY < -15 && binding.filterAppUsage.isExtended) { + binding.filterAppUsage.shrink() + } else if (oldScrollY > 15 && !binding.filterAppUsage.isExtended) { + binding.filterAppUsage.extend() + } else if (binding.appDataUsageRecycler.computeVerticalScrollOffset() == 0 && !binding.filterAppUsage.isExtended) { + binding.filterAppUsage.extend() + } + } + + dataUsageViewModel.userAppsList.observe(viewLifecycleOwner) { userAppsList -> + onDataLoaded(userAppsList) + adapter.differ.submitList(userAppsList) + } + + adapter.setOnItemClickListener { model -> + if (model.packageName == requireContext().getString(R.string.package_system)) { + val intent = Intent(context, ContainerActivity::class.java) + // intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra( + Values.GENERAL_FRAGMENT_ID, + Values.DATA_USAGE_SYSTEM + ) + intent.putExtra( + Values.DATA_USAGE_SESSION, + model.session + ) + intent.putExtra(Values.DATA_USAGE_TYPE, model.type) + requireContext().startActivity(intent) + } else setDataUsageSheetDialog(model) + } + } + + @Throws(ParseException::class, RemoteException::class) + private fun getTotalDataUsage(context: Context?): String { + val date = PreferenceManager.getDefaultSharedPreferences( + requireContext() + ).getInt(Values.DATA_RESET_DATE, -1) + return when (type) { + Values.TYPE_MOBILE_DATA -> NetworkStatsHelper.formatData( + NetworkStatsHelper.getTotalAppMobileDataUsage(context, session, date)[0], + NetworkStatsHelper.getTotalAppMobileDataUsage(context, session, date)[1] + )[2] + + Values.TYPE_WIFI -> NetworkStatsHelper.formatData( + NetworkStatsHelper.getTotalAppWifiDataUsage(context, session)[0], + NetworkStatsHelper.getTotalAppWifiDataUsage(context, session)[1] + )[2] + + else -> requireContext().getString(R.string.label_unknown) + } + } + + private fun onDataLoaded(userAppsList: List) { + try { + if (totalDataUsage?.isEmpty() == true) + totalDataUsage = getTotalDataUsage(context) + + binding.currentSessionTotal.text = + requireContext().getString(R.string.total_usage, totalDataUsage) + } catch (e: ParseException) { + e.printStackTrace() + } catch (e: RemoteException) { + e.printStackTrace() + } + binding.filterAppUsage.visibility = View.VISIBLE + binding.refreshDataUsage.isRefreshing = false + binding.layoutListLoading.root.visibility = View.GONE + binding.appDataUsageRecycler.animate().alpha(1.0f) + if (userAppsList.isEmpty()) { + binding.layoutListLoading.root.animate().alpha(0.0F) + binding.emptyList.animate().alpha(1.0F) + } else { + session = userAppsList[0]?.session ?: 0 + type = userAppsList[0]?.type ?: 0 + } + } + + private fun refreshData() { + binding.filterAppUsage.visibility = View.GONE + binding.layoutListLoading.root.visibility = View.VISIBLE + binding.appDataUsageRecycler.animate().alpha(0.0f) + binding.emptyList.animate().alpha(0.0f) + binding.refreshDataUsage.isRefreshing = true + binding.appDataUsageRecycler.removeAllViews() + totalDataUsage = "" + binding.currentSessionTotal.text = "..." + dataUsageViewModel.loadUserAppsData(session, type) + } + + @SuppressLint("InflateParams") + fun setDataUsageSheetDialog(model: AppDataUsageModel) { + val dialog = BottomSheetDialog(requireContext(), R.style.BottomSheet) + val dialogView = + LayoutInflater.from(context).inflate(R.layout.app_detail_view, null) + dialog.setContentView(dialogView) + val appIcon = dialogView.findViewById(R.id.icon) + val appName = dialogView.findViewById(R.id.name) + val dataSent = dialogView.findViewById(R.id.data_sent) + val dataReceived = dialogView.findViewById(R.id.data_received) + val appPackage = dialogView.findViewById(R.id.app_package) + val appUid = dialogView.findViewById(R.id.app_uid) + val appScreenTime = dialogView.findViewById(R.id.app_screen_time) + val appBackgroundTime = dialogView.findViewById(R.id.app_background_time) + val appCombinedTotal = dialogView.findViewById(R.id.app_combined_total) + val appSettings = dialogView.findViewById(R.id.app_open_settings) + appName.text = model.appName + val packageName: String = requireContext().resources.getString( + R.string.app_label_package_name, + model.packageName + ) + val uid: String = requireContext().resources.getString( + R.string.app_label_uid, + model.uid + ) + appPackage.text = setBoldSpan(packageName, model.packageName) + appUid.text = setBoldSpan(uid, model.uid.toString()) + if (model.packageName !== requireContext().getString(R.string.package_tethering)) { + val screenTime: String = requireContext().getString( + R.string.app_label_screen_time, + requireContext().getString(R.string.label_loading) + ) + val backgroundTime: String = requireContext().getString( + R.string.app_label_background_time, + requireContext().getString(R.string.label_loading) + ) + appScreenTime.text = setBoldSpan( + screenTime, + requireContext().getString(R.string.label_loading) + ) + appBackgroundTime.text = setBoldSpan( + backgroundTime, + requireContext().getString(R.string.label_loading) + ) + lifecycleScope.launch { + loadScreenTime(requireContext(), model, appScreenTime, appBackgroundTime) + } + } else { + appScreenTime.visibility = View.GONE + appBackgroundTime.visibility = View.GONE + } + val total = + model.sentMobile + model.receivedMobile + model.sentWifi + model.receivedWifi + val combinedTotal = NetworkStatsHelper.formatData(0L, total)[2] + dataSent.text = + NetworkStatsHelper.formatData(model.sentMobile, model.receivedMobile)[0] + dataReceived.text = + NetworkStatsHelper.formatData(model.sentMobile, model.receivedMobile)[1] + appCombinedTotal.text = setBoldSpan( + requireContext().getString(R.string.app_label_combined_total, combinedTotal), + combinedTotal + ) + appSettings.setOnClickListener { + val intent = + Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + val uri = Uri.fromParts("package", model.packageName, null) + intent.data = uri + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + requireContext().startActivity(intent) + } + try { + if (model.packageName == requireContext().getString(R.string.package_tethering)) { + appIcon.setImageResource(R.drawable.hotspot) + appPackage.visibility = View.GONE + appUid.visibility = View.GONE + appSettings.visibility = View.GONE + appCombinedTotal.visibility = View.GONE + } else if (model.packageName == requireContext().getString(R.string.package_removed)) { + appIcon.setImageResource(R.drawable.deleted_apps) + appPackage.visibility = View.GONE + appUid.visibility = View.GONE + appSettings.visibility = View.GONE + } else { + if (Common.isAppInstalled(context, model.packageName)) { + appIcon.setImageDrawable( + requireContext().packageManager.getApplicationIcon(model.packageName) + ) + } else { + appIcon.setImageResource(R.drawable.deleted_apps) + } + } + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + } + dialog.setOnShowListener { dialogInterface -> + val bottomSheetDialog = dialogInterface as BottomSheetDialog + val bottomSheet = + bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet) + bottomSheet?.let { + val behavior: BottomSheetBehavior<*> = + BottomSheetBehavior.from(bottomSheet) + behavior.state = BottomSheetBehavior.STATE_EXPANDED + behavior.skipCollapsed = true + } + } + dialog.show() + } + + override fun onDestroy() { + super.onDestroy() + _binding = null + } + + companion object { + private val TAG = AppDataUsageFragment::class.java.simpleName + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/drnoob/datamonitor/ui/fragments/HomeFragment.java b/app/src/main/java/com/drnoob/datamonitor/ui/fragments/HomeFragment.java index afcd4ed4..1bed52af 100644 --- a/app/src/main/java/com/drnoob/datamonitor/ui/fragments/HomeFragment.java +++ b/app/src/main/java/com/drnoob/datamonitor/ui/fragments/HomeFragment.java @@ -49,7 +49,6 @@ import static com.drnoob.datamonitor.core.Values.SHOW_ADD_PLAN_BANNER; import static com.drnoob.datamonitor.core.Values.TYPE_MOBILE_DATA; import static com.drnoob.datamonitor.core.Values.TYPE_WIFI; -import static com.drnoob.datamonitor.ui.activities.MainActivity.setRefreshAppDataUsage; import static com.drnoob.datamonitor.utils.NetworkStatsHelper.formatData; import static com.drnoob.datamonitor.utils.NetworkStatsHelper.getDeviceMobileDataUsage; import static com.drnoob.datamonitor.utils.NetworkStatsHelper.getDeviceWifiDataUsage; @@ -94,6 +93,7 @@ import com.drnoob.datamonitor.Widget.DataUsageWidget; import com.drnoob.datamonitor.adapters.data.OverviewModel; import com.drnoob.datamonitor.ui.activities.ContainerActivity; +import com.drnoob.datamonitor.ui.activities.MainActivity; import com.drnoob.datamonitor.utils.NotificationService; import com.drnoob.datamonitor.utils.SmartDataAllocationService; import com.drnoob.datamonitor.utils.VibrationUtils; @@ -374,7 +374,7 @@ public void onClick(View v) { intent.putExtra(DATA_USAGE_SESSION, SESSION_TODAY); intent.putExtra(DATA_USAGE_TYPE, TYPE_MOBILE_DATA); intent.putExtra(DAILY_DATA_HOME_ACTION, true); - setRefreshAppDataUsage(true); + MainActivity.Companion.setRefreshAppDataUsage(true); startActivity(intent); } }); @@ -387,7 +387,7 @@ public void onClick(View v) { intent.putExtra(DATA_USAGE_SESSION, SESSION_TODAY); intent.putExtra(DATA_USAGE_TYPE, TYPE_WIFI); intent.putExtra(DAILY_DATA_HOME_ACTION, true); - setRefreshAppDataUsage(true); + MainActivity.Companion.setRefreshAppDataUsage(true); startActivity(intent); } }); diff --git a/app/src/main/java/com/drnoob/datamonitor/ui/fragments/SystemDataUsageFragment.java b/app/src/main/java/com/drnoob/datamonitor/ui/fragments/SystemDataUsageFragment.java index 3d97cbf7..ef6efd84 100644 --- a/app/src/main/java/com/drnoob/datamonitor/ui/fragments/SystemDataUsageFragment.java +++ b/app/src/main/java/com/drnoob/datamonitor/ui/fragments/SystemDataUsageFragment.java @@ -22,14 +22,8 @@ import static com.drnoob.datamonitor.core.Values.DAILY_DATA_HOME_ACTION; import static com.drnoob.datamonitor.core.Values.DATA_USAGE_SESSION; import static com.drnoob.datamonitor.core.Values.DATA_USAGE_TYPE; -import static com.drnoob.datamonitor.core.Values.SESSION_ALL_TIME; -import static com.drnoob.datamonitor.core.Values.SESSION_LAST_MONTH; -import static com.drnoob.datamonitor.core.Values.SESSION_THIS_MONTH; -import static com.drnoob.datamonitor.core.Values.SESSION_THIS_YEAR; import static com.drnoob.datamonitor.core.Values.SESSION_TODAY; -import static com.drnoob.datamonitor.core.Values.SESSION_YESTERDAY; import static com.drnoob.datamonitor.core.Values.TYPE_MOBILE_DATA; -import static com.drnoob.datamonitor.core.Values.TYPE_WIFI; import static com.drnoob.datamonitor.utils.NetworkStatsHelper.getAppMobileDataUsage; import static com.drnoob.datamonitor.utils.NetworkStatsHelper.getAppWifiDataUsage; import static com.drnoob.datamonitor.utils.NetworkStatsHelper.getDeviceMobileDataUsage; @@ -45,12 +39,13 @@ import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; -import android.widget.RadioGroup; import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; @@ -58,10 +53,13 @@ import com.drnoob.datamonitor.R; import com.drnoob.datamonitor.adapters.AppDataUsageAdapter; import com.drnoob.datamonitor.adapters.data.AppDataUsageModel; +import com.drnoob.datamonitor.adapters.data.DataUsageViewModel; +import com.drnoob.datamonitor.adapters.data.DataUsageViewModelFactory; import com.drnoob.datamonitor.core.task.DatabaseHandler; -import com.google.android.material.bottomsheet.BottomSheetDialog; +import com.drnoob.datamonitor.utils.helpers.UsageDataHelperImpl; import java.text.ParseException; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -76,7 +74,7 @@ public class SystemDataUsageFragment extends Fragment { private static SwipeRefreshLayout mDataRefresh; private static TextView mSession, mType, mEmptyList; public static LinearLayout mTopBar; - private static List mList; + private static List mList = new ArrayList<>(); private static AppDataUsageAdapter mAdapter; public SystemDataUsageFragment() { @@ -99,8 +97,12 @@ public void onAttach(@NonNull Context context) { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // Inflate the layout for this fragment - View view = inflater.inflate(R.layout.fragment_system_data_usage, container, false); + return inflater.inflate(R.layout.fragment_system_data_usage, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); mAppsView = view.findViewById(R.id.app_data_usage_recycler); mLoading = view.findViewById(R.id.layout_list_loading); @@ -109,7 +111,6 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, // mType = view.findViewById(R.id.data_usage_type); mTopBar = view.findViewById(R.id.top_bar); mEmptyList = view.findViewById(R.id.empty_list); - mList = AppDataUsageFragment.mSystemList; mAdapter = new AppDataUsageAdapter(mList, mContext); mAdapter.setActivity(getActivity()); mAppsView = view.findViewById(R.id.app_data_usage_recycler); @@ -125,12 +126,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, mAppsView.setPadding(0, 20, 0, 0); } } - -// setSession(session); -// setType(type); - - - if (mList.size() > 0) { + if (mList != null && mList.size() > 0) { mLoading.setAlpha(0.0f); onDataLoaded(); } else { @@ -145,8 +141,6 @@ public void onRefresh() { loadData.execute(); } }); - - return view; } private static void onDataLoaded() { @@ -234,7 +228,6 @@ protected Object doInBackground(Object[] objects) { } model.setProgress(progress); - mList.add(model); } diff --git a/app/src/main/java/com/drnoob/datamonitor/utils/ScreenTimeUtils.kt b/app/src/main/java/com/drnoob/datamonitor/utils/ScreenTimeUtils.kt new file mode 100644 index 00000000..cbd0c9c8 --- /dev/null +++ b/app/src/main/java/com/drnoob/datamonitor/utils/ScreenTimeUtils.kt @@ -0,0 +1,150 @@ +package com.drnoob.datamonitor.utils + +import android.app.usage.UsageEvents +import android.app.usage.UsageStatsManager +import android.content.Context +import android.graphics.Typeface +import android.net.ParseException +import android.os.Build +import android.text.Spannable +import android.text.SpannableString +import android.text.style.StyleSpan +import android.view.View +import android.widget.TextView +import com.drnoob.datamonitor.R +import com.drnoob.datamonitor.adapters.data.AppDataUsageModel +import kotlin.math.roundToInt + +fun loadScreenTime( + context: Context, + model: AppDataUsageModel, + appScreenTime: TextView, + appBackgroundTime: TextView +) { + if (model.packageName !== context.getString(R.string.package_tethering)) { + val usageTime = getUsageTime(context, model.packageName, model.session) + if (usageTime[1] == -1) { + // If value is -1, build version is below Q + val screenTime: String = context.getString( + R.string.app_label_screen_time, + formatTime(context, usageTime[0] / 60f) + ) + appScreenTime.text = setBoldSpan(screenTime, formatTime(context, usageTime[0] / 60f)) + appBackgroundTime.visibility = View.GONE + } else { + val screenTime: String = context.getString( + R.string.app_label_screen_time, + formatTime(context, usageTime[0] / 60f) + ) + val backgroundTime: String = context.getString( + R.string.app_label_background_time, + formatTime(context, usageTime[0] / 60f) + ) + appScreenTime.text = setBoldSpan(screenTime, formatTime(context, usageTime[0] / 60f)) + appBackgroundTime.text = + setBoldSpan(backgroundTime, formatTime(context, usageTime[0] / 60f)) + } + } else { + appScreenTime.visibility = View.GONE + appBackgroundTime.visibility = View.GONE + } +} + +fun setBoldSpan(textString: String, spanText: String): SpannableString { + val boldSpan = SpannableString(textString) + val start = textString.indexOf(spanText).takeIf { it >= 0 } ?: 0 + val end = start + spanText.length + boldSpan.setSpan(StyleSpan(Typeface.BOLD), start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE) + return boldSpan +} + +/** + * Returns app usage time as an array like [screenTime, backgroundTime] + * ScreenTime source credit: https://stackoverflow.com/questions/61677505/how-to-count-app-usage-time-while-app-is-on-foreground + */ +private fun getUsageTime(context: Context, packageName: String, session: Int): IntArray { + + val allEvents: MutableList = mutableListOf() + val appScreenTime = HashMap().apply { put(packageName, 0) } + val appBackgroundTime = HashMap().apply { put(packageName, -1) } + val usageStatsManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager + val timePeriod = NetworkStatsHelper.getTimePeriod(context, session, 1) + + try { + val usageEvents = usageStatsManager.queryEvents(timePeriod[0], timePeriod[1]) + val usageStats = usageStatsManager.queryUsageStats( + UsageStatsManager.INTERVAL_DAILY, + timePeriod[0], timePeriod[1] + ) + + while (usageEvents.hasNextEvent()) { + val currentEvent = UsageEvents.Event() + usageEvents.getNextEvent(currentEvent) + if (currentEvent.packageName == packageName && currentEvent.eventType in setOf( + UsageEvents.Event.ACTIVITY_RESUMED, + UsageEvents.Event.ACTIVITY_PAUSED, + UsageEvents.Event.FOREGROUND_SERVICE_START, + UsageEvents.Event.FOREGROUND_SERVICE_STOP + ) + ) { + allEvents.add(currentEvent) + } + } + + if (allEvents.size > 0) { + allEvents.zipWithNext { event, nextEvent -> + if (event.eventType == UsageEvents.Event.ACTIVITY_RESUMED && + nextEvent.eventType == UsageEvents.Event.ACTIVITY_PAUSED && + event.className == nextEvent.className + ) { + val diff = (nextEvent.timeStamp - event.timeStamp).toInt() / 1000 + val prev = appScreenTime[event.packageName] ?: 0 + appScreenTime[event.packageName] = prev + diff + } + } + val lastEvent = allEvents.last() + if (lastEvent.eventType == UsageEvents.Event.ACTIVITY_RESUMED) { + val diff = (System.currentTimeMillis() - lastEvent.timeStamp) / 1000 + val prev = appScreenTime[lastEvent.packageName] ?: 0 + appScreenTime[lastEvent.packageName] = prev + diff.toInt() + } + } else { + appScreenTime[packageName] = 0 + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + if (!usageStats.isNullOrEmpty()) { + for (stats in usageStats) { + if (stats.packageName == packageName) { + val backgroundTime = stats.totalTimeForegroundServiceUsed.toInt() / 1000 + appBackgroundTime[packageName] = backgroundTime + break + } + } + } else { + appBackgroundTime[packageName] = 0 + } + } else { + appBackgroundTime[packageName] = 0 + } + } catch (e: ParseException) { + e.printStackTrace() + } + + return intArrayOf(appScreenTime[packageName]!!, appBackgroundTime[packageName]!!) +} + +private fun formatTime(context: Context, minutes: Float): String { + + if (minutes < 1 && minutes > 0) return "Less than a minute" + if (minutes >= 60) { + val hours = (minutes / 60).toInt() + val mins = (minutes % 60).toInt() + val hourLabel: String = if (hours > 1) "hours" else "hour" + val minuteLabel: String = if (mins == 1) "minute" else "minutes" + return context.getString(R.string.usage_time_label, hours, hourLabel, mins, minuteLabel) + } + return if (minutes == 1f) { + minutes.roundToInt().toString() + " minute" + } else minutes.roundToInt().toString() + " minutes" +} diff --git a/app/src/main/java/com/drnoob/datamonitor/utils/helpers/ThemeHelper.kt b/app/src/main/java/com/drnoob/datamonitor/utils/helpers/ThemeHelper.kt new file mode 100644 index 00000000..ead5250e --- /dev/null +++ b/app/src/main/java/com/drnoob/datamonitor/utils/helpers/ThemeHelper.kt @@ -0,0 +1,18 @@ +package com.drnoob.datamonitor.utils.helpers + +import android.app.Activity +import androidx.appcompat.app.AppCompatDelegate +import androidx.preference.PreferenceManager +import com.drnoob.datamonitor.core.Values + +fun setTheme(activity: Activity?) { + val theme = PreferenceManager.getDefaultSharedPreferences( + activity!! + ).getString(Values.APP_THEME, "system") + when (theme) { + "dark" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) + "light" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) + "system" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) + else -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/drnoob/datamonitor/utils/helpers/UsageDataHelper.kt b/app/src/main/java/com/drnoob/datamonitor/utils/helpers/UsageDataHelper.kt new file mode 100644 index 00000000..623ab6ea --- /dev/null +++ b/app/src/main/java/com/drnoob/datamonitor/utils/helpers/UsageDataHelper.kt @@ -0,0 +1,9 @@ +package com.drnoob.datamonitor.utils.helpers + +import com.drnoob.datamonitor.adapters.data.AppDataUsageModel + +interface UsageDataHelper { + suspend fun fetchApps() + suspend fun loadUserAppsData(session: Int, type: Int): MutableList + suspend fun loadSystemAppsData(session: Int, type: Int): MutableList +} \ No newline at end of file diff --git a/app/src/main/java/com/drnoob/datamonitor/utils/helpers/UsageDataHelperImpl.kt b/app/src/main/java/com/drnoob/datamonitor/utils/helpers/UsageDataHelperImpl.kt new file mode 100644 index 00000000..f8fe6a9b --- /dev/null +++ b/app/src/main/java/com/drnoob/datamonitor/utils/helpers/UsageDataHelperImpl.kt @@ -0,0 +1,367 @@ +package com.drnoob.datamonitor.utils.helpers + +import android.content.Context +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import android.os.RemoteException +import androidx.preference.PreferenceManager +import com.drnoob.datamonitor.Common +import com.drnoob.datamonitor.R +import com.drnoob.datamonitor.adapters.data.AppDataUsageModel +import com.drnoob.datamonitor.core.Values +import com.drnoob.datamonitor.core.task.DatabaseHandler +import com.drnoob.datamonitor.utils.NetworkStatsHelper +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.text.ParseException + +class UsageDataHelperImpl(val context: Context) : UsageDataHelper { + + override suspend fun fetchApps() = withContext(Dispatchers.IO) { + val packageManager = context.packageManager + val allApps = packageManager.getInstalledApplications(PackageManager.GET_META_DATA) + val modelList: MutableList = ArrayList() + var model: AppDataUsageModel? + val databaseHandler = DatabaseHandler(context) + if (allApps.size == databaseHandler.usageList.size) return@withContext + for (applicationInfo in allApps) { + if ((applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) == 1) { + // System app + modelList.add( + AppDataUsageModel( + packageManager.getApplicationLabel(applicationInfo).toString(), + applicationInfo.packageName, + applicationInfo.uid, + true + ) + ) + } else { + // User app + modelList.add( + AppDataUsageModel( + packageManager.getApplicationLabel(applicationInfo).toString(), + applicationInfo.packageName, + applicationInfo.uid, + false + ) + ) + } + } + for (i in modelList.indices) { + model = AppDataUsageModel().apply { + this.appName = modelList[i].appName + this.packageName = modelList[i].packageName + this.uid = modelList[i].uid + this.setIsSystemApp(modelList[i].isSystemApp) + } + databaseHandler.addData(model) + } + } + + override suspend fun loadUserAppsData( + session: Int, + type: Int + ): MutableList = withContext(Dispatchers.IO) { + val dataList = mutableListOf() + var totalSystemSent = 0L + var totalSystemReceived = 0L + val date = PreferenceManager.getDefaultSharedPreferences(context) + .getInt(Values.DATA_RESET_DATE, 1) + val handler = DatabaseHandler(context) + val list = handler.usageList + + for (currentApp in list) { + if (!Common.isAppInstalled( + context, + currentApp.packageName + ) + ) continue + val model: AppDataUsageModel? + val (sent, received) = when (type) { + Values.TYPE_MOBILE_DATA -> { + try { + val mobileDataUsage = NetworkStatsHelper.getAppMobileDataUsage( + context, + currentApp.uid, + session + ) + mobileDataUsage[0] to mobileDataUsage[1] + } catch (e: ParseException) { + e.printStackTrace() + continue + } catch (e: RemoteException) { + e.printStackTrace() + continue + } + } + else -> { + try { + val wifiDataUsage = NetworkStatsHelper.getAppWifiDataUsage( + context, + currentApp.uid, + session + ) + wifiDataUsage[0] to wifiDataUsage[1] + } catch (e: ParseException) { + e.printStackTrace() + continue + } catch (e: RemoteException) { + e.printStackTrace() + continue + } + } + } + + if (sent <= 0 && received <= 0) continue + if (currentApp.isSystemApp) { + totalSystemSent += sent + totalSystemReceived += received + continue + } + + model = AppDataUsageModel().apply { + appName = currentApp.appName + packageName = currentApp.packageName + uid = currentApp.uid + sentMobile = sent + receivedMobile = received + this.session = session + this.type = type + + val total = sent + received + val deviceTotal = when (type) { + Values.TYPE_MOBILE_DATA -> NetworkStatsHelper.getDeviceMobileDataUsage( + context, + session, + date + )[2] + + else -> NetworkStatsHelper.getDeviceWifiDataUsage(context, session)[2] + } + + progress = ((total.toDouble() / deviceTotal.toDouble()) * 100 * 2).toInt() + } + dataList.add(model) + } + + return@withContext modifyDataList( + dataList, + session, type, + totalSystemSent, + totalSystemReceived + ) + } + + + override suspend fun loadSystemAppsData( + session: Int, + type: Int + ): MutableList = withContext(Dispatchers.IO) { + val dataList = mutableListOf() + var totalSystemSent = 0L + var totalSystemReceived = 0L + val date = PreferenceManager.getDefaultSharedPreferences(context) + .getInt(Values.DATA_RESET_DATE, 1) + val handler = DatabaseHandler(context) + val list = handler.usageList + + for (currentApp in list) { + if (!currentApp.isSystemApp) continue + val model: AppDataUsageModel? + val (sent, received) = when (type) { + Values.TYPE_MOBILE_DATA -> { + try { + val mobileDataUsage = NetworkStatsHelper.getAppMobileDataUsage( + context, + currentApp.uid, + session + ) + mobileDataUsage[0] to mobileDataUsage[1] + } catch (e: ParseException) { + e.printStackTrace() + continue + } catch (e: RemoteException) { + e.printStackTrace() + continue + } + } + + else -> { + try { + val wifiDataUsage = NetworkStatsHelper.getAppWifiDataUsage( + context, + currentApp.uid, + session + ) + wifiDataUsage[0] to wifiDataUsage[1] + } catch (e: ParseException) { + e.printStackTrace() + continue + } catch (e: RemoteException) { + e.printStackTrace() + continue + } + } + } + + if (sent <= 0 && received <= 0) continue + + model = AppDataUsageModel().apply { + appName = currentApp.appName + packageName = currentApp.packageName + uid = currentApp.uid + sentMobile = sent + receivedMobile = received + this.session = session + this.type = type + + val total = sent + received + val deviceTotal = when (type) { + Values.TYPE_MOBILE_DATA -> NetworkStatsHelper.getDeviceMobileDataUsage( + context, + session, + date + )[2] + + else -> NetworkStatsHelper.getDeviceWifiDataUsage(context, session)[2] + } + + progress = ((total.toDouble() / deviceTotal.toDouble()) * 100 * 2).toInt() + + totalSystemSent += sent + totalSystemReceived += received + } + dataList.add(model) + } + + return@withContext modifyDataList( + dataList, + session, type, + totalSystemSent, + totalSystemReceived + ) + } + + private fun loadData( + session: Int, + type: Int, + totalSystemSent: Long, + totalSystemReceived: Long + ): MutableList { + val dataList = mutableListOf() + val totalTetheringSent: Long + val totalTetheringReceived: Long + val totalDeletedAppsSent: Long + val totalDeletedAppsReceived: Long + val tetheringTotal: Long + val deletedAppsTotal: Long + var deviceTotal = 0L + val date = PreferenceManager.getDefaultSharedPreferences(context) + .getInt(Values.DATA_RESET_DATE, 1) + + var model = AppDataUsageModel().apply { + this.appName = context.getString(R.string.label_system_apps) + this.packageName = context.getString(R.string.package_system) + this.sentMobile = totalSystemSent + this.receivedMobile = totalSystemReceived + this.session = session + this.type = type + val total = totalSystemSent + totalSystemReceived + if (type == Values.TYPE_MOBILE_DATA) { + try { + deviceTotal = + NetworkStatsHelper.getDeviceMobileDataUsage(context, session, date)[2] + this.progress = (total.toDouble() / deviceTotal.toDouble() * 100 * 2).toInt() + } catch (e: ParseException) { + e.printStackTrace() + } catch (e: RemoteException) { + e.printStackTrace() + } + } else { + try { + deviceTotal = NetworkStatsHelper.getDeviceWifiDataUsage(context, session)[2] + this.progress = (total.toDouble() / deviceTotal.toDouble() * 100 * 2).toInt() + } catch (e: ParseException) { + e.printStackTrace() + } catch (e: RemoteException) { + e.printStackTrace() + } + } + } + if (deviceTotal > 0) dataList.add(model) + try { + if (type == Values.TYPE_MOBILE_DATA) { + totalTetheringSent = NetworkStatsHelper.getTetheringDataUsage(context, session)[0] + totalTetheringReceived = + NetworkStatsHelper.getTetheringDataUsage(context, session)[1] + tetheringTotal = totalTetheringSent + totalTetheringReceived + val tetheringProgress = tetheringTotal.toDouble() / deviceTotal.toDouble() * 100 * 2 + val tetheringProgressInt: Int = tetheringProgress.toInt() + model = AppDataUsageModel().apply { + this.appName = context.getString(R.string.label_tethering) + this.packageName = context.getString(R.string.package_tethering) + this.sentMobile = totalTetheringSent + this.receivedMobile = totalTetheringReceived + this.session = session + this.type = type + this.progress = tetheringProgressInt + } + if (tetheringTotal > 0) dataList.add(model) + totalDeletedAppsSent = + NetworkStatsHelper.getDeletedAppsMobileDataUsage(context, session)[0] + totalDeletedAppsReceived = NetworkStatsHelper.getDeletedAppsMobileDataUsage( + context, session + )[1] + } else { + totalDeletedAppsSent = + NetworkStatsHelper.getDeletedAppsWifiDataUsage(context, session)[0] + totalDeletedAppsReceived = NetworkStatsHelper.getDeletedAppsWifiDataUsage( + context, session + )[1] + } + deletedAppsTotal = totalDeletedAppsSent + totalDeletedAppsReceived + val deletedProgress = deletedAppsTotal.toDouble() / deviceTotal.toDouble() * 100 * 2 + val deletedProgressInt: Int = deletedProgress.toInt() + + model = AppDataUsageModel().apply { + this.packageName = context.getString(R.string.package_removed) + this.appName = context.getString(R.string.label_removed) + this.sentMobile = totalDeletedAppsSent + this.receivedMobile = totalDeletedAppsReceived + this.session = session + this.type = type + this.progress = deletedProgressInt + } + if (deletedAppsTotal > 0) dataList.add(model) + } catch (e: ParseException) { + e.printStackTrace() + } catch (e: RemoteException) { + e.printStackTrace() + } + return dataList + } + + private fun modifyDataList( + dataList: MutableList, + session: Int, + type: Int, + totalSystemSent: Long, + totalSystemReceived: Long + ): MutableList { + dataList.addAll(loadData(session, type, totalSystemSent, totalSystemReceived)) + dataList.sortWith { o1, o2 -> + o1!!.mobileTotal = (o1.sentMobile + o1.receivedMobile) / 1024f + o2!!.mobileTotal = (o2.sentMobile + o2.receivedMobile) / 1024f + o1.mobileTotal.compareTo(o2.mobileTotal) + } + dataList.reverse() + dataList.sortWith { o1, o2 -> + o1!!.mobileTotal = (o1.sentMobile + o1.receivedMobile) / 1024f + o2!!.mobileTotal = (o2.sentMobile + o2.receivedMobile) / 1024f + o1.mobileTotal.compareTo(o2.mobileTotal) + } + dataList.reverse() + + return dataList + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_app_data_usage.xml b/app/src/main/res/layout/fragment_app_data_usage.xml index b268b7c8..61d37101 100644 --- a/app/src/main/res/layout/fragment_app_data_usage.xml +++ b/app/src/main/res/layout/fragment_app_data_usage.xml @@ -101,7 +101,7 @@ layout="@layout/app_data_usage_loading" android:layout_width="match_parent" android:layout_height="wrap_content" - android:alpha="1" + android:visibility="gone" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/top_bar" />