1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-09-27 16:56:57 +02:00

Introduce bottom navigation bar to replace drawer

This commit is contained in:
Arjan Schrijver 2023-05-11 22:26:42 +02:00
parent e4cac887cc
commit 9c86976118
21 changed files with 788 additions and 658 deletions

View File

@ -111,7 +111,7 @@
android:theme="@style/GadgetbridgeTheme"
tools:replace="android:label">
<activity
android:name=".activities.ControlCenterv2"
android:name=".activities.MainActivity"
android:label="@string/title_activity_controlcenter"
android:theme="@style/SplashTheme"
android:launchMode="singleTop"
@ -127,7 +127,7 @@
<activity
android:name=".activities.SettingsActivity"
android:label="@string/title_activity_settings"
android:parentActivityName=".activities.ControlCenterv2" />
android:parentActivityName=".activities.MainActivity" />
<activity
android:name=".activities.AboutUserPreferencesActivity"
android:label="@string/activity_prefs_about_you"
@ -163,12 +163,12 @@
<activity
android:name=".activities.ActivitySummariesActivity"
android:label="@string/activity_summaries"
android:parentActivityName=".activities.ControlCenterv2" />
android:parentActivityName=".activities.MainActivity" />
<activity
android:name=".activities.appmanager.AppManagerActivity"
android:label="@string/title_activity_appmanager"
android:launchMode="singleTop"
android:parentActivityName=".activities.ControlCenterv2" />
android:parentActivityName=".activities.MainActivity" />
<activity
android:name=".activities.AppBlacklistActivity"
android:label="@string/title_activity_notification_management"
@ -180,11 +180,11 @@
<activity
android:name=".devices.vesc.VescControlActivity"
android:label="@string/devicetype_vesc"
android:parentActivityName=".activities.ControlCenterv2" />
android:parentActivityName=".activities.MainActivity" />
<activity
android:name=".activities.FwAppInstallerActivity"
android:label="@string/title_activity_fw_app_insaller"
android:parentActivityName=".activities.ControlCenterv2"
android:parentActivityName=".activities.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@ -515,23 +515,23 @@
<activity
android:name=".activities.DebugActivity"
android:label="@string/title_activity_debug"
android:parentActivityName=".activities.ControlCenterv2"
android:parentActivityName=".activities.MainActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden" />
<activity
android:name=".activities.AboutActivity"
android:label="@string/about_activity_title"
android:parentActivityName=".activities.ControlCenterv2"
android:parentActivityName=".activities.MainActivity"
android:windowSoftInputMode="stateHidden" />
<activity
android:name=".activities.OpenFwAppInstallerActivity"
android:label="@string/open_fw_installer_info_text_title"
android:parentActivityName=".activities.ControlCenterv2"
android:parentActivityName=".activities.MainActivity"
android:windowSoftInputMode="stateHidden" />
<activity
android:name=".activities.BatteryInfoActivity"
android:label="@string/battery_detail_activity_title"
android:parentActivityName=".activities.ControlCenterv2" />
android:parentActivityName=".activities.MainActivity" />
<activity
android:name=".activities.ActivitySummaryDetail"
@ -547,17 +547,17 @@
<activity
android:name=".activities.DataManagementActivity"
android:label="@string/title_activity_db_management"
android:parentActivityName=".activities.ControlCenterv2"
android:parentActivityName=".activities.MainActivity"
android:windowSoftInputMode="stateHidden" />
<activity
android:name=".activities.NotificationManagementActivity"
android:label="@string/title_activity_notification_management"
android:parentActivityName=".activities.ControlCenterv2"
android:parentActivityName=".activities.MainActivity"
android:windowSoftInputMode="stateHidden" />
<activity
android:name=".activities.discovery.DiscoveryActivityV2"
android:label="@string/title_activity_discovery"
android:parentActivityName=".activities.ControlCenterv2" />
android:parentActivityName=".activities.MainActivity" />
<activity
android:name=".activities.AndroidPairingActivity"
android:label="@string/title_activity_android_pairing" />
@ -566,7 +566,7 @@
android:label="@string/title_activity_appmanager"
android:launchMode="singleTop"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|layoutDirection|uiMode"
android:parentActivityName=".activities.ControlCenterv2" />
android:parentActivityName=".activities.MainActivity" />
<activity
android:name=".devices.miband.MiBandPairingActivity"
android:label="@string/title_activity_mi_band_pairing" />
@ -588,15 +588,15 @@
<activity
android:name=".activities.charts.ActivityChartsActivity"
android:label="@string/title_activity_charts"
android:parentActivityName=".activities.ControlCenterv2" />
android:parentActivityName=".activities.MainActivity" />
<activity
android:name=".activities.ConfigureAlarms"
android:label="@string/title_activity_set_alarm"
android:parentActivityName=".activities.ControlCenterv2" />
android:parentActivityName=".activities.MainActivity" />
<activity
android:name=".activities.ConfigureReminders"
android:label="@string/title_activity_set_reminders"
android:parentActivityName=".activities.ControlCenterv2" />
android:parentActivityName=".activities.MainActivity" />
<activity
android:name=".activities.ConfigureContacts"
android:label="@string/title_activity_set_contacts"
@ -608,11 +608,11 @@
<activity
android:name=".activities.ConfigureWorldClocks"
android:label="@string/pref_world_clocks_title"
android:parentActivityName=".activities.ControlCenterv2" />
android:parentActivityName=".activities.MainActivity" />
<activity
android:name=".activities.devicesettings.DeviceSettingsActivity"
android:label="@string/title_activity_device_specific_settings"
android:parentActivityName=".activities.ControlCenterv2" />
android:parentActivityName=".activities.MainActivity" />
<activity
android:name=".activities.AlarmDetails"
android:label="@string/title_activity_alarm_details"
@ -645,7 +645,7 @@
<activity
android:name=".activities.VibrationActivity"
android:label="@string/title_activity_vibration"
android:parentActivityName=".activities.ControlCenterv2" />
android:parentActivityName=".activities.MainActivity" />
<activity
android:name=".activities.NotificationFilterActivity"
android:label="@string/title_activity_notification_filter"
@ -745,7 +745,7 @@
android:name=".devices.qhybrid.ConfigActivity"
android:label="@string/qhybrid_title_watchface"
android:exported="true"
android:parentActivityName=".activities.ControlCenterv2" />
android:parentActivityName=".activities.MainActivity" />
<activity
android:name=".devices.qhybrid.QHybridAppChoserActivity"
android:label="@string/qhybrid_title_apps"
@ -755,7 +755,7 @@
android:name=".devices.qhybrid.HRConfigActivity"
android:label="@string/qhybrid_title_watchface"
android:exported="true"
android:parentActivityName=".activities.ControlCenterv2" />
android:parentActivityName=".activities.MainActivity" />
<activity android:name=".devices.qhybrid.WidgetSettingsActivity"
android:label="@string/add_widget"
android:parentActivityName=".devices.qhybrid.HRConfigActivity" />

View File

@ -40,7 +40,7 @@ import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.concurrent.TimeUnit;
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
import nodomain.freeyourgadget.gadgetbridge.activities.MainActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.WidgetAlarmsActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ActivityChartsActivity;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
@ -98,7 +98,7 @@ public class Widget extends AppWidgetProvider {
views.setOnClickPendingIntent(R.id.todaywidget_header_container, refreshDataIntent);
//open GB main window
Intent startMainIntent = new Intent(context, ControlCenterv2.class);
Intent startMainIntent = new Intent(context, MainActivity.class);
PendingIntent startMainPIntent = PendingIntentUtils.getActivity(context, 0, startMainIntent, 0, false);
views.setOnClickPendingIntent(R.id.todaywidget_header_icon, startMainPIntent);

View File

@ -29,7 +29,6 @@ import android.app.NotificationManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
@ -42,8 +41,11 @@ import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.TypedValue;
import android.view.MenuItem;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Toast;
import android.view.ViewGroup;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
@ -61,13 +63,14 @@ import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.DialogFragment;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.appbar.MaterialToolbar;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.navigation.NavigationView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -77,11 +80,7 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
@ -93,149 +92,43 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.DailyTotals;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GBChangeLog;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
//TODO: extend AbstractGBActivity, but it requires actionbar that is not available
public class ControlCenterv2 extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener, GBActivity {
private static final Logger LOG = LoggerFactory.getLogger(ControlCenterv2.class);
public static final int MENU_REFRESH_CODE = 1;
public static final String ACTION_REQUEST_PERMISSIONS
= "nodomain.freeyourgadget.gadgetbridge.activities.controlcenter.requestpermissions";
public static final String ACTION_REQUEST_LOCATION_PERMISSIONS
= "nodomain.freeyourgadget.gadgetbridge.activities.controlcenter.requestlocationpermissions";
private static PhoneStateListener fakeStateListener;
//needed for KK compatibility
static {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
public class ControlCenterv2 extends Fragment {
private DeviceManager deviceManager;
private GBDeviceAdapterv2 mGBDeviceAdapter;
private RecyclerView deviceListView;
private FloatingActionButton fab;
private boolean isLanguageInvalid = false;
private boolean isThemeInvalid = false;
List<GBDevice> deviceList;
private HashMap<String,long[]> deviceActivityHashMap = new HashMap();
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (Objects.requireNonNull(action)) {
case GBApplication.ACTION_LANGUAGE_CHANGE:
setLanguage(GBApplication.getLanguage(), true);
break;
case GBApplication.ACTION_THEME_CHANGE:
isThemeInvalid = true;
break;
case GBApplication.ACTION_QUIT:
finish();
break;
case DeviceManager.ACTION_DEVICES_CHANGED:
case GBApplication.ACTION_NEW_DATA:
createRefreshTask("get activity data", getApplication()).execute();
mGBDeviceAdapter.rebuildFolders();
refreshPairedDevices();
break;
case DeviceService.ACTION_REALTIME_SAMPLES:
handleRealtimeSample(intent.getSerializableExtra(DeviceService.EXTRA_REALTIME_SAMPLE));
break;
case ACTION_REQUEST_PERMISSIONS:
checkAndRequestPermissions();
break;
case ACTION_REQUEST_LOCATION_PERMISSIONS:
checkAndRequestLocationPermissions();
break;
}
}
};
private boolean pesterWithPermissions = true;
private ActivitySample currentHRSample;
public ActivitySample getCurrentHRSample() {
return currentHRSample;
}
private void setCurrentHRSample(ActivitySample sample) {
if (HeartRateUtils.getInstance().isValidHeartRateValue(sample.getHeartRate())) {
currentHRSample = sample;
refreshPairedDevices();
}
}
private void handleRealtimeSample(Serializable extra) {
if (extra instanceof ActivitySample) {
ActivitySample sample = (ActivitySample) extra;
setCurrentHRSample(sample);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
AbstractGBActivity.init(this, AbstractGBActivity.NO_ACTIONBAR);
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
View currentView = inflater.inflate(R.layout.activity_controlcenterv2_content_main, container, false);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_controlcenterv2);
MaterialToolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
deviceManager = ((GBApplication) getActivity().getApplication()).getDeviceManager();
if (GBApplication.areDynamicColorsEnabled()) {
TypedValue typedValue = new TypedValue();
Resources.Theme theme = getTheme();
theme.resolveAttribute(R.attr.colorSurface, typedValue, true);
@ColorInt int toolbarBackground = typedValue.data;
toolbar.setBackgroundColor(toolbarBackground);
} else {
toolbar.setBackgroundColor(getResources().getColor(R.color.primarydark_light));
toolbar.setTitleTextColor(getResources().getColor(android.R.color.white));
}
DrawerLayout drawer = findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.controlcenter_navigation_drawer_open, R.string.controlcenter_navigation_drawer_close);
drawer.setDrawerListener(toggle);
toggle.syncState();
/* This sucks but for the play store we're not allowed a donation link. Instead for
the Bangle.js Play Store app we put a message in the About dialog via @string/about_description */
if (BuildConfig.FLAVOR == "banglejs") {
MenuItemImpl v = (MenuItemImpl) ((NavigationView) drawer.getChildAt(1)).getMenu().findItem(R.id.donation_link);
if (v != null) v.setVisible(false);
}
NavigationView navigationView = findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
//end of material design boilerplate
deviceManager = ((GBApplication) getApplication()).getDeviceManager();
deviceListView = findViewById(R.id.deviceListView);
deviceListView = currentView.findViewById(R.id.deviceListView);
deviceListView.setHasFixedSize(true);
deviceListView.setLayoutManager(new LinearLayoutManager(this));
deviceListView.setLayoutManager(new LinearLayoutManager(currentView.getContext()));
deviceList = deviceManager.getDevices();
mGBDeviceAdapter = new GBDeviceAdapterv2(this, deviceList, deviceActivityHashMap);
mGBDeviceAdapter = new GBDeviceAdapterv2(currentView.getContext(), deviceList, deviceActivityHashMap);
mGBDeviceAdapter.setHasStableIds(true);
// get activity data asynchronously, this fills the deviceActivityHashMap
// and calls refreshPairedDevices() notifyDataSetChanged
createRefreshTask("get activity data", getApplication()).execute();
deviceListView.setAdapter(this.mGBDeviceAdapter);
fab = findViewById(R.id.fab);
// get activity data asynchronously, this fills the deviceActivityHashMap
// and calls refreshPairedDevices() notifyDataSetChanged
createRefreshTask("get activity data", getActivity().getApplication()).execute();
fab = currentView.findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -282,211 +175,19 @@ public class ControlCenterv2 extends AppCompatActivity
registerForContextMenu(deviceListView);
IntentFilter filterLocal = new IntentFilter();
filterLocal.addAction(GBApplication.ACTION_LANGUAGE_CHANGE);
filterLocal.addAction(GBApplication.ACTION_THEME_CHANGE);
filterLocal.addAction(GBApplication.ACTION_QUIT);
filterLocal.addAction(GBApplication.ACTION_NEW_DATA);
filterLocal.addAction(DeviceManager.ACTION_DEVICES_CHANGED);
filterLocal.addAction(DeviceService.ACTION_REALTIME_SAMPLES);
filterLocal.addAction(ACTION_REQUEST_PERMISSIONS);
filterLocal.addAction(ACTION_REQUEST_LOCATION_PERMISSIONS);
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filterLocal);
refreshPairedDevices();
/*
* Ask for permission to intercept notifications on first run.
*/
Prefs prefs = GBApplication.getPrefs();
pesterWithPermissions = prefs.getBoolean("permission_pestering", true);
boolean displayPermissionDialog = !prefs.getBoolean("permission_dialog_displayed", false);
prefs.getPreferences().edit().putBoolean("permission_dialog_displayed", true).apply();
Set<String> set = NotificationManagerCompat.getEnabledListenerPackages(this);
if (pesterWithPermissions) {
if (!set.contains(this.getPackageName())) { // If notification listener access hasn't been granted
// Put up a dialog explaining why we need permissions (Polite, but also Play Store policy)
// When accepted, we open the Activity for Notification access
DialogFragment dialog = new NotifyListenerPermissionsDialogFragment();
dialog.show(getSupportFragmentManager(), "NotifyListenerPermissionsDialogFragment");
}
}
/* We not put up dialogs explaining why we need permissions (Polite, but also Play Store policy).
Rather than chaining the calls, we just open a bunch of dialogs. Last in this list = first
on the page, and as they are accepted the permissions are requested in turn.
When accepted, we request it or open the Activity for permission to display over other apps. */
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
/* In order to be able to set ringer mode to silent in GB's PhoneCallReceiver
the permission to access notifications is needed above Android M
ACCESS_NOTIFICATION_POLICY is also needed in the manifest */
if (pesterWithPermissions) {
if (!((NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE)).isNotificationPolicyAccessGranted()) {
// Put up a dialog explaining why we need permissions (Polite, but also Play Store policy)
// When accepted, we open the Activity for Notification access
DialogFragment dialog = new NotifyPolicyPermissionsDialogFragment();
dialog.show(getSupportFragmentManager(), "NotifyPolicyPermissionsDialogFragment");
}
}
if (!Settings.canDrawOverlays(getApplicationContext())) {
// If diplay over other apps access hasn't been granted
// Put up a dialog explaining why we need permissions (Polite, but also Play Store policy)
// When accepted, we open the Activity for permission to display over other apps.
if (pesterWithPermissions) {
DialogFragment dialog = new DisplayOverOthersPermissionsDialogFragment();
dialog.show(getSupportFragmentManager(), "DisplayOverOthersPermissionsDialogFragment");
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_DENIED) {
if (pesterWithPermissions) {
DialogFragment dialog = new LocationPermissionsDialogFragment();
dialog.show(getSupportFragmentManager(), "LocationPermissionsDialogFragment");
}
}
// Check all the other permissions that we need to for Android M + later
if (getWantedPermissions().isEmpty())
displayPermissionDialog = false;
if (displayPermissionDialog && pesterWithPermissions) {
DialogFragment dialog = new PermissionsDialogFragment();
dialog.show(getSupportFragmentManager(), "PermissionsDialogFragment");
// when 'ok' clicked, checkAndRequestPermissions() is called
} else
checkAndRequestPermissions();
}
GBChangeLog cl = createChangeLog();
final boolean showChangelog = prefs.getBoolean("show_changelog", true);
if (showChangelog && cl.isFirstRun() && cl.hasChanges(cl.isFirstRunEver())) {
try {
cl.getMaterialLogDialog().show();
} catch (Exception ignored) {
GB.toast(getBaseContext(), "Error showing Changelog", Toast.LENGTH_LONG, GB.ERROR);
}
}
if (GB.isBluetoothEnabled() && deviceList.isEmpty() && Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
launchDiscoveryActivity();
startActivity(new Intent(getActivity(), DiscoveryActivity.class));
} else {
GBApplication.deviceService().requestDeviceInfo();
}
}
@Override
protected void onResume() {
super.onResume();
handleShortcut(getIntent());
if (isLanguageInvalid || isThemeInvalid) {
isLanguageInvalid = false;
isThemeInvalid = false;
recreate();
}
}
@Override
protected void onDestroy() {
unregisterForContextMenu(deviceListView);
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
super.onDestroy();
}
@Override
public void onBackPressed() {
DrawerLayout drawer = findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == MENU_REFRESH_CODE) {
showFabIfNeccessary();
}
}
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
DrawerLayout drawer = findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
switch (item.getItemId()) {
case R.id.action_settings:
Intent settingsIntent = new Intent(this, SettingsActivity.class);
startActivityForResult(settingsIntent, MENU_REFRESH_CODE);
return false; //we do not want the drawer menu item to get selected
case R.id.action_debug:
Intent debugIntent = new Intent(this, DebugActivity.class);
startActivity(debugIntent);
return false;
case R.id.action_data_management:
Intent dbIntent = new Intent(this, DataManagementActivity.class);
startActivity(dbIntent);
return false;
case R.id.action_notification_management:
Intent blIntent = new Intent(this, NotificationManagementActivity.class);
startActivity(blIntent);
return false;
case R.id.device_action_discover:
launchDiscoveryActivity();
return false;
case R.id.action_quit:
GBApplication.quit();
return false;
case R.id.donation_link:
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("https://liberapay.com/Gadgetbridge")); //TODO: centralize if ever used somewhere else
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
return false;
case R.id.external_changelog:
GBChangeLog cl = createChangeLog();
try {
if (cl.hasChanges(false)) {
cl.getMaterialLogDialog().show();
} else {
cl.getMaterialFullLogDialog().show();
}
} catch (Exception ignored) {
GB.toast(getBaseContext(), "Error showing Changelog", Toast.LENGTH_LONG, GB.ERROR);
}
return false;
case R.id.about:
Intent aboutIntent = new Intent(this, AboutActivity.class);
startActivity(aboutIntent);
return false;
}
return false;
}
private GBChangeLog createChangeLog() {
String css = GBChangeLog.DEFAULT_CSS;
css += "body { "
+ "color: " + AndroidUtils.getTextColorHex(getBaseContext()) + "; "
+ "}";
return new GBChangeLog(this, css);
return currentView;
}
private void launchDiscoveryActivity() {
startActivity(new Intent(this, DiscoveryActivityV2.class));
}
private void refreshPairedDevices() {
mGBDeviceAdapter.notifyDataSetChanged();
startActivity(new Intent(getActivity(), DiscoveryActivity.class));
}
private void showFabIfNeccessary() {
@ -501,150 +202,10 @@ public class ControlCenterv2 extends AppCompatActivity
}
}
private void checkAndRequestLocationPermissions() {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED) {
LOG.error("No permission to access background location!");
toast(ControlCenterv2.this, getString(R.string.error_no_location_access), Toast.LENGTH_SHORT, GB.ERROR);
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION}, 0);
}
}
@TargetApi(Build.VERSION_CODES.M)
private List<String> getWantedPermissions() {
List<String> wantedPermissions = new ArrayList<>();
if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.BLUETOOTH);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_ADMIN) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.BLUETOOTH_ADMIN);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.READ_CONTACTS);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.CALL_PHONE);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.READ_CALL_LOG);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.READ_PHONE_STATE);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.PROCESS_OUTGOING_CALLS) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.PROCESS_OUTGOING_CALLS);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECEIVE_SMS) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.RECEIVE_SMS);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.READ_SMS);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.SEND_SMS) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.SEND_SMS);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.READ_CALENDAR);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.ACCESS_COARSE_LOCATION);
try {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.MEDIA_CONTENT_CONTROL) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.MEDIA_CONTENT_CONTROL);
} catch (Exception ignored) {
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (pesterWithPermissions) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ANSWER_PHONE_CALLS) == PackageManager.PERMISSION_DENIED) {
wantedPermissions.add(Manifest.permission.ANSWER_PHONE_CALLS);
}
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && Build.VERSION.SDK_INT <= Build.VERSION_CODES.S) {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_DENIED) {
wantedPermissions.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
}
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_DENIED) {
wantedPermissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.QUERY_ALL_PACKAGES) == PackageManager.PERMISSION_DENIED) {
wantedPermissions.add(Manifest.permission.QUERY_ALL_PACKAGES);
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_DENIED) {
wantedPermissions.add(Manifest.permission.BLUETOOTH_SCAN);
}
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_DENIED) {
wantedPermissions.add(Manifest.permission.BLUETOOTH_CONNECT);
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_DENIED) {
wantedPermissions.add(Manifest.permission.POST_NOTIFICATIONS);
}
}
if (BuildConfig.INTERNET_ACCESS) {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.INTERNET) == PackageManager.PERMISSION_DENIED) {
wantedPermissions.add(Manifest.permission.INTERNET);
}
}
return wantedPermissions;
}
@TargetApi(Build.VERSION_CODES.M)
private void checkAndRequestPermissions() {
List<String> wantedPermissions = getWantedPermissions();
if (!wantedPermissions.isEmpty()) {
Prefs prefs = GBApplication.getPrefs();
// If this is not the first run, we can rely on
// shouldShowRequestPermissionRationale(String permission)
// and ignore permissions that shouldn't or can't be requested again
if (prefs.getBoolean("permissions_asked", false)) {
// Don't request permissions that we shouldn't show a prompt for
// e.g. permissions that are "Never" granted by the user or never granted by the system
Set<String> shouldNotAsk = new HashSet<>();
for (String wantedPermission : wantedPermissions) {
if (!shouldShowRequestPermissionRationale(wantedPermission)) {
shouldNotAsk.add(wantedPermission);
}
}
wantedPermissions.removeAll(shouldNotAsk);
} else {
// Permissions have not been asked yet, but now will be
prefs.getPreferences().edit().putBoolean("permissions_asked", true).apply();
}
if (!wantedPermissions.isEmpty()) {
GB.toast(this, getString(R.string.permission_granting_mandatory), Toast.LENGTH_LONG, GB.ERROR);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
ActivityCompat.requestPermissions(this, wantedPermissions.toArray(new String[0]), 0);
} else {
requestMultiplePermissionsLauncher.launch(wantedPermissions.toArray(new String[0]));
}
}
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { // The enclosed hack in it's current state cause crash on Banglejs builds tarkgetSDK=31 on a Android 13 device.
// HACK: On Lineage we have to do this so that the permission dialog pops up
if (fakeStateListener == null) {
fakeStateListener = new PhoneStateListener();
TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
telephonyManager.listen(fakeStateListener, PhoneStateListener.LISTEN_CALL_STATE);
telephonyManager.listen(fakeStateListener, PhoneStateListener.LISTEN_NONE);
}
}
}
public void setLanguage(Locale language, boolean invalidateLanguage) {
if (invalidateLanguage) {
isLanguageInvalid = true;
}
AndroidUtils.setLanguage(this, language);
@Override
public void onDestroy() {
unregisterForContextMenu(deviceListView);
super.onDestroy();
}
private long[] getSteps(GBDevice device, DBHandler db) {
@ -654,7 +215,12 @@ public class ControlCenterv2 extends AppCompatActivity
return ds.getDailyTotalsForDevice(device, day, db);
}
protected RefreshTask createRefreshTask(String task, Context context) {
public void refreshPairedDevices() {
mGBDeviceAdapter.notifyDataSetChanged();
mGBDeviceAdapter.rebuildFolders();
}
public RefreshTask createRefreshTask(String task, Context context) {
return new RefreshTask(task, context);
}
@ -691,135 +257,4 @@ public class ControlCenterv2 extends AppCompatActivity
}
}
/// Called from onCreate - this puts up a dialog explaining we need permissions, and goes to the correct Activity
public static class NotifyPolicyPermissionsDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the Builder class for convenient dialog construction
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity());
final Context context = getContext();
builder.setMessage(context.getString(R.string.permission_notification_policy_access,
getContext().getString(R.string.app_name),
getContext().getString(R.string.ok)))
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.M)
public void onClick(DialogInterface dialog, int id) {
try {
startActivity(new Intent(android.provider.Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS));
} catch (ActivityNotFoundException e) {
GB.toast(context, "'Notification Policy' activity not found", Toast.LENGTH_LONG, GB.ERROR);
}
}
});
return builder.create();
}
}
/// Called from onCreate - this puts up a dialog explaining we need permissions, and goes to the correct Activity
public static class NotifyListenerPermissionsDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the Builder class for convenient dialog construction
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity());
final Context context = getContext();
builder.setMessage(context.getString(R.string.permission_notification_listener,
getContext().getString(R.string.app_name),
getContext().getString(R.string.ok)))
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
try {
startActivity(new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"));
} catch (ActivityNotFoundException e) {
GB.toast(context, "'Notification Listener Settings' activity not found", Toast.LENGTH_LONG, GB.ERROR);
}
}
});
return builder.create();
}
}
/// Called from onCreate - this puts up a dialog explaining we need permissions, and goes to the correct Activity
public static class DisplayOverOthersPermissionsDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the Builder class for convenient dialog construction
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity());
Context context = getContext();
builder.setMessage(context.getString(R.string.permission_display_over_other_apps,
getContext().getString(R.string.app_name),
getContext().getString(R.string.ok)))
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.M)
public void onClick(DialogInterface dialog, int id) {
Intent enableIntent = new Intent(android.provider.Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
startActivity(enableIntent);
}
}).setNegativeButton(R.string.dismiss, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {}
});
return builder.create();
}
}
/// Called from onCreate - this puts up a dialog explaining we need backgound location permissions, and then requests permissions when 'ok' pressed
public static class LocationPermissionsDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the Builder class for convenient dialog construction
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity());
Context context = getContext();
builder.setMessage(context.getString(R.string.permission_location,
getContext().getString(R.string.app_name),
getContext().getString(R.string.ok)))
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Intent intent = new Intent(ACTION_REQUEST_LOCATION_PERMISSIONS);
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
}
});
return builder.create();
}
}
// Register the permissions callback, which handles the user's response to the
// system permissions dialog. Save the return value, an instance of
// ActivityResultLauncher, as an instance variable.
// This is required here rather than where it is used because it'll cause a
// "LifecycleOwners must call register before they are STARTED" if not called from onCreate
public ActivityResultLauncher<String[]> requestMultiplePermissionsLauncher =
registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), isGranted -> {
if (isGranted.containsValue(true)) {
// Permission is granted. Continue the action or workflow in your
// app.
} else {
// Explain to the user that the feature is unavailable because the
// feature requires a permission that the user has denied. At the
// same time, respect the user's decision. Don't link to system
// settings in an effort to convince the user to change their
// decision.
GB.toast(this, getString(R.string.permission_granting_mandatory), Toast.LENGTH_LONG, GB.ERROR);
}
});
/// Called from onCreate - this puts up a dialog explaining we need permissions, and then requests permissions when 'ok' pressed
public static class PermissionsDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the Builder class for convenient dialog construction
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity());
Context context = getContext();
builder.setMessage(context.getString(R.string.permission_request,
getContext().getString(R.string.app_name),
getContext().getString(R.string.ok)))
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
}
});
return builder.create();
}
}
}

View File

@ -0,0 +1,19 @@
package nodomain.freeyourgadget.gadgetbridge.activities;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import nodomain.freeyourgadget.gadgetbridge.R;
public class DashboardFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_dashboard, container, false);
}
}

View File

@ -0,0 +1,488 @@
/* Copyright (C) 2016-2023 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, Johannes Tysiak, Taavi Eomäe, vanous, Arjan Schrijver
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.activities;
import android.Manifest;
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.NotificationManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.view.MenuItem;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.DialogFragment;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
public class MainActivity extends AbstractGBActivity implements BottomNavigationView.OnNavigationItemSelectedListener, GBActivity {
public static final String ACTION_REQUEST_PERMISSIONS
= "nodomain.freeyourgadget.gadgetbridge.activities.controlcenter.requestpermissions";
private boolean isLanguageInvalid = false;
private static PhoneStateListener fakeStateListener;
BottomNavigationView bottomNavigationView;
DashboardFragment dashboardFragment = new DashboardFragment();
ControlCenterv2 devicesFragment = new ControlCenterv2();
MainMenuFragment mainMenuFragment = new MainMenuFragment();
//needed for KK compatibility
static {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (Objects.requireNonNull(action)) {
case GBApplication.ACTION_LANGUAGE_CHANGE:
setLanguage(GBApplication.getLanguage(), true);
break;
case GBApplication.ACTION_QUIT:
finish();
break;
case DeviceManager.ACTION_DEVICES_CHANGED:
case GBApplication.ACTION_NEW_DATA:
if (devicesFragment.isResumed()) {
devicesFragment.createRefreshTask("get activity data", getApplication()).execute();
// mGBDeviceAdapter.rebuildFolders();
devicesFragment.refreshPairedDevices();
}
break;
case DeviceService.ACTION_REALTIME_SAMPLES:
handleRealtimeSample(intent.getSerializableExtra(DeviceService.EXTRA_REALTIME_SAMPLE));
break;
case ACTION_REQUEST_PERMISSIONS:
checkAndRequestPermissions(false);
break;
}
}
};
private boolean pesterWithPermissions = true;
private ActivitySample currentHRSample;
public ActivitySample getCurrentHRSample() {
return currentHRSample;
}
private void setCurrentHRSample(ActivitySample sample) {
if (HeartRateUtils.getInstance().isValidHeartRateValue(sample.getHeartRate())) {
currentHRSample = sample;
if (devicesFragment.isResumed()) {
devicesFragment.refreshPairedDevices();
}
}
}
private void handleRealtimeSample(Serializable extra) {
if (extra instanceof ActivitySample) {
ActivitySample sample = (ActivitySample) extra;
setCurrentHRSample(sample);
}
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bottomNavigationView = findViewById(R.id.bottom_nav_bar);
bottomNavigationView.setOnNavigationItemSelectedListener(this);
// TODO: read last view from savedInstanceState
bottomNavigationView.setSelectedItemId(R.id.bottom_nav_devices);
IntentFilter filterLocal = new IntentFilter();
filterLocal.addAction(GBApplication.ACTION_LANGUAGE_CHANGE);
filterLocal.addAction(GBApplication.ACTION_QUIT);
filterLocal.addAction(GBApplication.ACTION_NEW_DATA);
filterLocal.addAction(DeviceManager.ACTION_DEVICES_CHANGED);
filterLocal.addAction(DeviceService.ACTION_REALTIME_SAMPLES);
filterLocal.addAction(ACTION_REQUEST_PERMISSIONS);
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filterLocal);
/*
* Ask for permission to intercept notifications on first run.
*/
Prefs prefs = GBApplication.getPrefs();
pesterWithPermissions = prefs.getBoolean("permission_pestering", true);
Set<String> set = NotificationManagerCompat.getEnabledListenerPackages(this);
if (pesterWithPermissions) {
if (!set.contains(this.getPackageName())) { // If notification listener access hasn't been granted
// Put up a dialog explaining why we need permissions (Polite, but also Play Store policy)
// When accepted, we open the Activity for Notification access
DialogFragment dialog = new MainActivity.NotifyListenerPermissionsDialogFragment();
dialog.show(getSupportFragmentManager(), "NotifyListenerPermissionsDialogFragment");
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
/* In order to be able to set ringer mode to silent in GB's PhoneCallReceiver
the permission to access notifications is needed above Android M
ACCESS_NOTIFICATION_POLICY is also needed in the manifest */
if (pesterWithPermissions) {
if (!((NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE)).isNotificationPolicyAccessGranted()) {
// Put up a dialog explaining why we need permissions (Polite, but also Play Store policy)
// When accepted, we open the Activity for Notification access
DialogFragment dialog = new MainActivity.NotifyPolicyPermissionsDialogFragment();
dialog.show(getSupportFragmentManager(), "NotifyPolicyPermissionsDialogFragment");
}
}
if (!android.provider.Settings.canDrawOverlays(getApplicationContext())) {
// If diplay over other apps access hasn't been granted
// Put up a dialog explaining why we need permissions (Polite, but also Play Store policy)
// When accepted, we open the Activity for permission to display over other apps.
if (pesterWithPermissions) {
DialogFragment dialog = new MainActivity.DisplayOverOthersPermissionsDialogFragment();
dialog.show(getSupportFragmentManager(), "DisplayOverOthersPermissionsDialogFragment");
}
}
// Check all the other permissions that we need to for Android M + later
checkAndRequestPermissions(true);
}
}
@Override
protected void onResume() {
super.onResume();
if (isLanguageInvalid) {
isLanguageInvalid = false;
recreate();
}
}
@Override
protected void onDestroy() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
super.onDestroy();
}
@Override
public boolean
onNavigationItemSelected(@NonNull MenuItem item)
{
// TODO: save current view so we can restore it in onCreate()
switch (item.getItemId()) {
case R.id.bottom_nav_dashboard:
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, dashboardFragment)
.commit();
return true;
case R.id.bottom_nav_devices:
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, devicesFragment)
.commit();
return true;
case R.id.bottom_nav_menu:
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, mainMenuFragment)
.commit();
return true;
}
return false;
}
@TargetApi(Build.VERSION_CODES.M)
private void checkAndRequestPermissions(boolean showDialogFirst) {
List<String> wantedPermissions = new ArrayList<>();
if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.BLUETOOTH);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_ADMIN) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.BLUETOOTH_ADMIN);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.READ_CONTACTS);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.CALL_PHONE);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.READ_CALL_LOG);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.READ_PHONE_STATE);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.PROCESS_OUTGOING_CALLS) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.PROCESS_OUTGOING_CALLS);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECEIVE_SMS) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.RECEIVE_SMS);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.READ_SMS);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.SEND_SMS) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.SEND_SMS);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.READ_CALENDAR);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.ACCESS_COARSE_LOCATION);
try {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.MEDIA_CONTENT_CONTROL) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.MEDIA_CONTENT_CONTROL);
} catch (Exception ignored) {
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (pesterWithPermissions) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ANSWER_PHONE_CALLS) == PackageManager.PERMISSION_DENIED) {
wantedPermissions.add(Manifest.permission.ANSWER_PHONE_CALLS);
}
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && Build.VERSION.SDK_INT <= Build.VERSION_CODES.S) {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_DENIED) {
wantedPermissions.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.QUERY_ALL_PACKAGES) == PackageManager.PERMISSION_DENIED) {
wantedPermissions.add(Manifest.permission.QUERY_ALL_PACKAGES);
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_DENIED) {
wantedPermissions.add(Manifest.permission.BLUETOOTH_SCAN);
}
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_DENIED) {
wantedPermissions.add(Manifest.permission.BLUETOOTH_CONNECT);
}
}
if (BuildConfig.INTERNET_ACCESS) {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.INTERNET) == PackageManager.PERMISSION_DENIED) {
wantedPermissions.add(Manifest.permission.INTERNET);
}
}
if (!wantedPermissions.isEmpty()) {
Prefs prefs = GBApplication.getPrefs();
// If this is not the first run, we can rely on
// shouldShowRequestPermissionRationale(String permission)
// and ignore permissions that shouldn't or can't be requested again
if (prefs.getBoolean("permissions_asked", false)) {
// Don't request permissions that we shouldn't show a prompt for
// e.g. permissions that are "Never" granted by the user or never granted by the system
Set<String> shouldNotAsk = new HashSet<>();
for (String wantedPermission : wantedPermissions) {
if (!shouldShowRequestPermissionRationale(wantedPermission)) {
shouldNotAsk.add(wantedPermission);
}
}
wantedPermissions.removeAll(shouldNotAsk);
} else if (!showDialogFirst) {
// Permissions have not been asked yet, but now will be
prefs.getPreferences().edit().putBoolean("permissions_asked", true).apply();
}
if (!wantedPermissions.isEmpty()) {
if (showDialogFirst) {
// Show a dialog - this will then call checkAndRequestPermissions(false)
DialogFragment dialog = new LocationPermissionsDialogFragment();
dialog.show(getSupportFragmentManager(), "LocationPermissionsDialogFragment");
//requestMultiplePermissionsLauncher.launch(wantedPermissions.toArray(new String[0]));
} else {
GB.toast(this, getString(R.string.permission_granting_mandatory), Toast.LENGTH_LONG, GB.ERROR);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
ActivityCompat.requestPermissions(this, wantedPermissions.toArray(new String[0]), 0);
} else {
requestMultiplePermissionsLauncher.launch(wantedPermissions.toArray(new String[0]));
//ActivityCompat.requestPermissions(this, wantedPermissions.toArray(new String[0]), 0); //Actually this still works if I test it, not sure if the new way is more reliable or not...
}
}
}
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { // The enclosed hack in it's current state cause crash on Banglejs builds tarkgetSDK=31 on a Android 13 device.
// HACK: On Lineage we have to do this so that the permission dialog pops up
if (fakeStateListener == null) {
fakeStateListener = new PhoneStateListener();
TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
telephonyManager.listen(fakeStateListener, PhoneStateListener.LISTEN_CALL_STATE);
telephonyManager.listen(fakeStateListener, PhoneStateListener.LISTEN_NONE);
}
}
}
public void setLanguage(Locale language, boolean invalidateLanguage) {
if (invalidateLanguage) {
isLanguageInvalid = true;
}
AndroidUtils.setLanguage(this, language);
}
/// Called from onCreate - this puts up a dialog explaining we need permissions, and goes to the correct Activity
public static class NotifyPolicyPermissionsDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the Builder class for convenient dialog construction
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
final Context context = getContext();
builder.setMessage(context.getString(R.string.permission_notification_policy_access,
getContext().getString(R.string.app_name),
getContext().getString(R.string.ok)))
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.M)
public void onClick(DialogInterface dialog, int id) {
try {
startActivity(new Intent(android.provider.Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS));
} catch (ActivityNotFoundException e) {
GB.toast(context, "'Notification Policy' activity not found", Toast.LENGTH_LONG, GB.ERROR);
}
}
});
return builder.create();
}
}
/// Called from onCreate - this puts up a dialog explaining we need permissions, and goes to the correct Activity
public static class NotifyListenerPermissionsDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the Builder class for convenient dialog construction
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
final Context context = getContext();
builder.setMessage(context.getString(R.string.permission_notification_listener,
getContext().getString(R.string.app_name),
getContext().getString(R.string.ok)))
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
try {
startActivity(new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"));
} catch (ActivityNotFoundException e) {
GB.toast(context, "'Notification Listener Settings' activity not found", Toast.LENGTH_LONG, GB.ERROR);
}
}
});
return builder.create();
}
}
/// Called from onCreate - this puts up a dialog explaining we need permissions, and goes to the correct Activity
public static class DisplayOverOthersPermissionsDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the Builder class for convenient dialog construction
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
Context context = getContext();
builder.setMessage(context.getString(R.string.permission_display_over_other_apps,
getContext().getString(R.string.app_name),
getContext().getString(R.string.ok)))
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.M)
public void onClick(DialogInterface dialog, int id) {
Intent enableIntent = new Intent(android.provider.Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
startActivity(enableIntent);
}
}).setNegativeButton(R.string.dismiss, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {}
});
return builder.create();
}
}
/// Called from checkAndRequestPermissions - this puts up a dialog explaining we need permissions, and then calls checkAndRequestPermissions (via an intent) when 'ok' pressed
public static class LocationPermissionsDialogFragment extends DialogFragment {
ControlCenterv2 controlCenter;
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the Builder class for convenient dialog construction
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
Context context = getContext();
builder.setMessage(context.getString(R.string.permission_location,
getContext().getString(R.string.app_name),
getContext().getString(R.string.ok)))
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
}
});
return builder.create();
}
}
// Register the permissions callback, which handles the user's response to the
// system permissions dialog. Save the return value, an instance of
// ActivityResultLauncher, as an instance variable.
public ActivityResultLauncher<String[]> requestMultiplePermissionsLauncher =
registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), isGranted -> {
if (isGranted.containsValue(true)) {
// Permission is granted. Continue the action or workflow in your
// app.
} else {
// Explain to the user that the feature is unavailable because the
// feature requires a permission that the user has denied. At the
// same time, respect the user's decision. Don't link to system
// settings in an effort to convince the user to change their
// decision.
GB.toast(this, getString(R.string.permission_granting_mandatory), Toast.LENGTH_LONG, GB.ERROR);
}
});
}

View File

@ -0,0 +1,104 @@
package nodomain.freeyourgadget.gadgetbridge.activities;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.view.menu.MenuItemImpl;
import androidx.fragment.app.Fragment;
import com.google.android.material.navigation.NavigationView;
import de.cketti.library.changelog.ChangeLog;
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class MainMenuFragment extends Fragment implements NavigationView.OnNavigationItemSelectedListener {
public static final int MENU_REFRESH_CODE = 1;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View currentView = inflater.inflate(R.layout.fragment_main_menu, container, false);
NavigationView navigationView = currentView.findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
/* This sucks but for the play store we're not allowed a donation link. Instead for
the Bangle.js Play Store app we put a message in the About dialog via @string/about_description */
if (BuildConfig.FLAVOR == "banglejs") {
MenuItemImpl v = (MenuItemImpl) navigationView.getMenu().findItem(R.id.donation_link);
if (v != null) v.setVisible(false);
}
return currentView;
}
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.action_settings:
Intent settingsIntent = new Intent(getActivity(), SettingsActivity.class);
startActivityForResult(settingsIntent, MENU_REFRESH_CODE);
return false; //we do not want the drawer menu item to get selected
case R.id.action_debug:
Intent debugIntent = new Intent(getActivity(), DebugActivity.class);
startActivity(debugIntent);
return false;
case R.id.action_data_management:
Intent dbIntent = new Intent(getActivity(), DataManagementActivity.class);
startActivity(dbIntent);
return false;
case R.id.action_notification_management:
Intent blIntent = new Intent(getActivity(), NotificationManagementActivity.class);
startActivity(blIntent);
return false;
case R.id.device_action_discover:
launchDiscoveryActivity();
return false;
case R.id.action_quit:
GBApplication.quit();
return false;
case R.id.donation_link:
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("https://liberapay.com/Gadgetbridge")); //TODO: centralize if ever used somewhere else
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
return false;
case R.id.external_changelog:
ChangeLog cl = createChangeLog();
try {
cl.getLogDialog().show();
} catch (Exception ignored) {
GB.toast(getActivity().getBaseContext(), "Error showing Changelog", Toast.LENGTH_LONG, GB.ERROR);
}
return false;
case R.id.about:
Intent aboutIntent = new Intent(getActivity(), AboutActivity.class);
startActivity(aboutIntent);
return false;
}
return false;
}
private ChangeLog createChangeLog() {
String css = ChangeLog.DEFAULT_CSS;
css += "body { "
+ "color: " + AndroidUtils.getTextColorHex(getActivity().getBaseContext()) + "; "
+ "background-color: " + AndroidUtils.getBackgroundColorHex(getActivity().getBaseContext()) + ";" +
"}";
return new ChangeLog(getContext(), css);
}
private void launchDiscoveryActivity() {
startActivity(new Intent(getActivity(), DiscoveryActivity.class));
}
}

View File

@ -102,8 +102,8 @@ import nodomain.freeyourgadget.gadgetbridge.activities.ActivitySummariesActivity
import nodomain.freeyourgadget.gadgetbridge.activities.BatteryInfoActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.ConfigureAlarms;
import nodomain.freeyourgadget.gadgetbridge.activities.ConfigureReminders;
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateDialog;
import nodomain.freeyourgadget.gadgetbridge.activities.MainActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.OpenFwAppInstallerActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.VibrationActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ActivityChartsActivity;
@ -396,8 +396,8 @@ public class GBDeviceAdapterv2 extends ListAdapter<GBDevice, GBDeviceAdapterv2.V
}
}
holder.heartRateStatusBox.setVisibility((device.isInitialized() && coordinator.supportsRealtimeData() && coordinator.supportsManualHeartRateMeasurement(device)) ? View.VISIBLE : View.GONE);
if (parent.getContext() instanceof ControlCenterv2) {
ActivitySample sample = ((ControlCenterv2) parent.getContext()).getCurrentHRSample();
if (parent.getContext() instanceof MainActivity) {
ActivitySample sample = ((MainActivity) parent.getContext()).getCurrentHRSample();
if (sample != null) {
holder.heartRateStatusLabel.setText(String.valueOf(sample.getHeartRate()));
} else {

View File

@ -30,7 +30,7 @@ import androidx.annotation.StringRes;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
import nodomain.freeyourgadget.gadgetbridge.activities.MainActivity;
import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
@ -118,7 +118,7 @@ public class UnknownDeviceCoordinator extends AbstractDeviceCoordinator {
@Override
public Class<? extends Activity> getPairingActivity() {
return ControlCenterv2.class;
return MainActivity.class;
}
@Override

View File

@ -35,7 +35,7 @@ import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
import nodomain.freeyourgadget.gadgetbridge.activities.MainActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.discovery.DiscoveryActivityV2;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
@ -101,7 +101,7 @@ public class LenovoWatchPairingActivity extends AbstractGBActivity implements Bo
@Override
public void onBondingComplete(boolean success) {
startActivity(new Intent(this, ControlCenterv2.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
startActivity(new Intent(this, MainActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
finish();
}

View File

@ -41,7 +41,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.AboutUserPreferencesActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
import nodomain.freeyourgadget.gadgetbridge.activities.MainActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.discovery.DiscoveryActivityV2;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
@ -173,7 +173,7 @@ public class MiBandPairingActivity extends AbstractGBActivity implements Bonding
Prefs prefs = GBApplication.getPrefs();
prefs.getPreferences().edit().putString(MiBandConst.PREF_MIBAND_ADDRESS, macAddress).apply();
}
Intent intent = new Intent(this, ControlCenterv2.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
Intent intent = new Intent(this, MainActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
finish();

View File

@ -41,7 +41,7 @@ import de.greenrobot.dao.query.Query;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
import nodomain.freeyourgadget.gadgetbridge.activities.MainActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.discovery.DiscoveryActivityV2;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
@ -170,7 +170,7 @@ public class PebblePairingActivity extends AbstractGBActivity implements Bonding
public void onBondingComplete(boolean success) {
unregisterBroadcastReceivers();
if (success) {
startActivity(new Intent(this, ControlCenterv2.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
startActivity(new Intent(this, MainActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
} else {
startActivity(new Intent(this, DiscoveryActivityV2.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
}

View File

@ -34,7 +34,7 @@ import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
import nodomain.freeyourgadget.gadgetbridge.activities.MainActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.discovery.DiscoveryActivityV2;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
@ -93,7 +93,7 @@ public class Watch9PairingActivity extends AbstractGBActivity implements Bonding
@Override
public void onBondingComplete(boolean success) {
startActivity(new Intent(this, ControlCenterv2.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
startActivity(new Intent(this, MainActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
finish();
}

View File

@ -55,7 +55,7 @@ import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBEnvironment;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
import nodomain.freeyourgadget.gadgetbridge.activities.MainActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.SettingsActivity;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventScreenshot;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
@ -157,7 +157,7 @@ public class GB {
}
private static PendingIntent getContentIntent(Context context) {
Intent notificationIntent = new Intent(context, ControlCenterv2.class);
Intent notificationIntent = new Intent(context, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntentUtils.getActivity(context, 0,
@ -465,7 +465,7 @@ public class GB {
private static Notification createTransferNotification(String title, String text, boolean ongoing,
int percentage, Context context) {
Intent notificationIntent = new Intent(context, ControlCenterv2.class);
Intent notificationIntent = new Intent(context, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntentUtils.getActivity(context, 0,
@ -501,7 +501,7 @@ public class GB {
}
public static void createGpsNotification(Context context, int numDevices) {
Intent notificationIntent = new Intent(context, ControlCenterv2.class);
Intent notificationIntent = new Intent(context, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntentUtils.getActivity(context, 0, notificationIntent, 0, false);
@ -523,7 +523,7 @@ public class GB {
private static Notification createInstallNotification(String text, boolean ongoing,
int percentage, Context context) {
Intent notificationIntent = new Intent(context, ControlCenterv2.class);
Intent notificationIntent = new Intent(context, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntentUtils.getActivity(context, 0,
@ -553,7 +553,7 @@ public class GB {
}
private static Notification createBatteryNotification(String text, String bigText, Context context) {
Intent notificationIntent = new Intent(context, ControlCenterv2.class);
Intent notificationIntent = new Intent(context, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntentUtils.getActivity(context, 0,

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#7E7E7E"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#000000"
android:pathData="M3,13h8L11,3L3,3v10zM3,21h8v-6L3,15v6zM13,21h8L21,11h-8v10zM13,3v6h8L21,3h-8z" />
</vector>

View File

@ -5,10 +5,7 @@
android:id="@+id/content_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2"
tools:showIn="@layout/activity_controlcenterv2_app_bar_main">
tools:context=".activities.ControlCenterv2">
<ImageView
android:id="@+id/no_items_bg"
@ -26,4 +23,14 @@
android:layout_centerHorizontal="true"
android:divider="@null" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_gravity="bottom|end"
app:srcCompat="@drawable/ic_add"
android:layout_margin="16dp" />
</RelativeLayout>

View File

@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2">
tools:context="nodomain.freeyourgadget.gadgetbridge.activities.MainActivity">
<ScrollView
android:id="@+id/scrollView2"

View File

@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2">
tools:context="nodomain.freeyourgadget.gadgetbridge.activities.MainActivity">
<ScrollView
android:id="@+id/scrollView"

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/bottom_nav_bar"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav_bar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/bottom_nav_menu" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.DashboardFragment">
<ImageView
android:id="@+id/no_items_bg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:alpha="0.1"
android:tint="?attr/textColorPrimary"
app:srcCompat="@drawable/gadgetbridge_img" />
<TextView
android:id="@+id/text_home"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="80dp"
android:text="Dashboard when?"
android:textAlignment="center"
android:textSize="20sp"
/>
</RelativeLayout>

View File

@ -1,25 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<include
layout="@layout/activity_controlcenterv2_app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
tools:context=".activities.MainMenuFragment">
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_main"
app:menu="@menu/activity_controlcenterv2_main_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:title="Dashboard"
android:id="@+id/bottom_nav_dashboard"
android:icon="@drawable/ic_dashboard"/>
<item
android:title="Devices"
android:id="@+id/bottom_nav_devices"
android:icon="@drawable/ic_devices_other"/>
<item
android:title="Menu"
android:id="@+id/bottom_nav_menu"
android:icon="@drawable/ic_menu"/>
</menu>