diff --git a/CHANGELOG.md b/CHANGELOG.md
index ebd0ff297..a73f51392 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,8 @@
####Version 0.10.2
* Pebble: allow to manually paste configuration data for legacy configuration pages
* Pebble: various improvements to the configuration page
+* Pebble: Suppport FW 4.0-dp1 and Pebble2 emulator (needs recompilation of Gadgetbridge)
+* Pebble: Fix a problem with key events when using the Pebble music player
####Version 0.10.1
* Pebble: set extended music info by dissecting notifications on Android 5.0+
diff --git a/app/build.gradle b/app/build.gradle
index 607ee74b9..3f6389ce6 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -18,8 +18,8 @@ android {
targetSdkVersion 23
// note: always bump BOTH versionCode and versionName!
- versionName "0.10.1"
- versionCode 54
+ versionName "0.10.2"
+ versionCode 55
}
buildTypes {
release {
@@ -60,6 +60,7 @@ dependencies {
compile 'de.cketti.library.changelog:ckchangelog:1.2.2'
compile 'net.e175.klaus:solarpositioning:0.0.9'
compile 'com.github.freeyourgadget:greendao:c3830951e5dd3d1e63d7bac600d5f773b81df363'
+ compile 'com.github.woxthebox:draglistview:1.2.6'
}
preBuild.dependsOn(":GBDaoGenerator:genSources")
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4535b4b30..97599340a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -55,7 +55,7 @@
android:parentActivityName=".activities.SettingsActivity" />
+ android:parentActivityName=".activities.appmanager.AppManagerActivity">
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java
deleted file mode 100644
index a6dae1b2d..000000000
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java
+++ /dev/null
@@ -1,286 +0,0 @@
-package nodomain.freeyourgadget.gadgetbridge.activities;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.v4.app.NavUtils;
-import android.support.v4.content.LocalBroadcastManager;
-import android.view.ContextMenu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ListView;
-
-import org.json.JSONObject;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.UUID;
-
-import nodomain.freeyourgadget.gadgetbridge.GBApplication;
-import nodomain.freeyourgadget.gadgetbridge.R;
-import nodomain.freeyourgadget.gadgetbridge.adapter.GBDeviceAppAdapter;
-import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
-import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
-import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
-import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleProtocol;
-import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
-import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils;
-import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
-
-
-public class AppManagerActivity extends GBActivity {
- public static final String ACTION_REFRESH_APPLIST
- = "nodomain.freeyourgadget.gadgetbridge.appmanager.action.refresh_applist";
- private static final Logger LOG = LoggerFactory.getLogger(AppManagerActivity.class);
-
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(GBApplication.ACTION_QUIT)) {
- finish();
- } else if (action.equals(ACTION_REFRESH_APPLIST)) {
- int appCount = intent.getIntExtra("app_count", 0);
- for (Integer i = 0; i < appCount; i++) {
- String appName = intent.getStringExtra("app_name" + i.toString());
- String appCreator = intent.getStringExtra("app_creator" + i.toString());
- UUID uuid = UUID.fromString(intent.getStringExtra("app_uuid" + i.toString()));
- GBDeviceApp.Type appType = GBDeviceApp.Type.values()[intent.getIntExtra("app_type" + i.toString(), 0)];
-
- boolean found = false;
- for (final ListIterator iter = appList.listIterator(); iter.hasNext(); ) {
- final GBDeviceApp app = iter.next();
- if (app.getUUID().equals(uuid)) {
- app.setOnDevice(true);
- iter.set(app);
- found = true;
- break;
- }
- }
- if (!found) {
- GBDeviceApp app = new GBDeviceApp(uuid, appName, appCreator, "", appType);
- app.setOnDevice(true);
- appList.add(app);
- }
- }
-
- mGBDeviceAppAdapter.notifyDataSetChanged();
- }
- }
- };
-
- private Prefs prefs;
-
- private final List appList = new ArrayList<>();
- private GBDeviceAppAdapter mGBDeviceAppAdapter;
- private GBDeviceApp selectedApp = null;
- private GBDevice mGBDevice = null;
-
- private List getSystemApps() {
- List systemApps = new ArrayList<>();
- if (prefs.getBoolean("pebble_force_untested", false)) {
- systemApps.add(new GBDeviceApp(UUID.fromString("4dab81a6-d2fc-458a-992c-7a1f3b96a970"), "Sports (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
- systemApps.add(new GBDeviceApp(UUID.fromString("cf1e816a-9db0-4511-bbb8-f60c48ca8fac"), "Golf (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
- }
- if (mGBDevice != null && !"aplite".equals(PebbleUtils.getPlatformName(mGBDevice.getHardwareVersion()))) {
- systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
- }
-
- return systemApps;
- }
-
- private List getCachedApps() {
- List cachedAppList = new ArrayList<>();
- File cachePath;
- try {
- cachePath = new File(FileUtils.getExternalFilesDir().getPath() + "/pbw-cache");
- } catch (IOException e) {
- LOG.warn("could not get external dir while reading pbw cache.");
- return cachedAppList;
- }
-
- File files[] = cachePath.listFiles();
- if (files != null) {
- for (File file : files) {
- if (file.getName().endsWith(".pbw")) {
- String baseName = file.getName().substring(0, file.getName().length() - 4);
- //metadata
- File jsonFile = new File(cachePath, baseName + ".json");
- //configuration
- File configFile = new File(cachePath, baseName + "_config.js");
- try {
- String jsonstring = FileUtils.getStringFromFile(jsonFile);
- JSONObject json = new JSONObject(jsonstring);
- cachedAppList.add(new GBDeviceApp(json, configFile.exists()));
- } catch (Exception e) {
- LOG.warn("could not read json file for " + baseName, e.getMessage(), e);
- cachedAppList.add(new GBDeviceApp(UUID.fromString(baseName), baseName, "N/A", "", GBDeviceApp.Type.UNKNOWN));
- }
- }
- }
- }
- return cachedAppList;
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- Bundle extras = getIntent().getExtras();
- if (extras != null) {
- mGBDevice = extras.getParcelable(GBDevice.EXTRA_DEVICE);
- } else {
- throw new IllegalArgumentException("Must provide a device when invoking this activity");
- }
-
- prefs = GBApplication.getPrefs();
-
- setContentView(R.layout.activity_appmanager);
-
- ListView appListView = (ListView) findViewById(R.id.appListView);
- mGBDeviceAppAdapter = new GBDeviceAppAdapter(this, appList);
- appListView.setAdapter(this.mGBDeviceAppAdapter);
-
- appListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView parent, View v, int position, long id) {
- UUID uuid = appList.get(position).getUUID();
- GBApplication.deviceService().onAppStart(uuid, true);
- }
- });
-
- registerForContextMenu(appListView);
-
- appList.addAll(getCachedApps());
-
- appList.addAll(getSystemApps());
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(GBApplication.ACTION_QUIT);
- filter.addAction(ACTION_REFRESH_APPLIST);
-
- LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter);
-
- GBApplication.deviceService().onAppInfoReq();
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, v, menuInfo);
- getMenuInflater().inflate(R.menu.appmanager_context, menu);
- AdapterView.AdapterContextMenuInfo acmi = (AdapterView.AdapterContextMenuInfo) menuInfo;
- selectedApp = appList.get(acmi.position);
-
- if (!selectedApp.isInCache()) {
- menu.removeItem(R.id.appmanager_app_reinstall);
- menu.removeItem(R.id.appmanager_app_delete_cache);
- }
- if (!PebbleProtocol.UUID_PEBBLE_HEALTH.equals(selectedApp.getUUID())) {
- menu.removeItem(R.id.appmanager_health_activate);
- menu.removeItem(R.id.appmanager_health_deactivate);
- }
- if (selectedApp.getType() == GBDeviceApp.Type.APP_SYSTEM) {
- menu.removeItem(R.id.appmanager_app_delete);
- }
- if (!selectedApp.isConfigurable()) {
- menu.removeItem(R.id.appmanager_app_configure);
- }
- if (mGBDevice != null && !mGBDevice.getFirmwareVersion().startsWith("v3")) {
- menu.removeItem(R.id.appmanager_app_move_to_top);
- }
- menu.setHeaderTitle(selectedApp.getName());
- }
-
- private void removeAppFromList(UUID uuid) {
- for (final ListIterator iter = appList.listIterator(); iter.hasNext(); ) {
- final GBDeviceApp app = iter.next();
- if (app.getUUID().equals(uuid)) {
- iter.remove();
- mGBDeviceAppAdapter.notifyDataSetChanged();
- break;
- }
- }
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.appmanager_health_deactivate:
- case R.id.appmanager_app_delete_cache:
- String baseName;
- try {
- baseName = FileUtils.getExternalFilesDir().getPath() + "/pbw-cache/" + selectedApp.getUUID();
- } catch (IOException e) {
- LOG.warn("could not get external dir while trying to access pbw cache.");
- return true;
- }
-
- String[] suffixToDelete = new String[]{".pbw", ".json", "_config.js"};
-
- for (String suffix : suffixToDelete) {
- File fileToDelete = new File(baseName + suffix);
- if (!fileToDelete.delete()) {
- LOG.warn("could not delete file from pbw cache: " + fileToDelete.toString());
- } else {
- LOG.info("deleted file: " + fileToDelete.toString());
- }
- }
- removeAppFromList(selectedApp.getUUID());
- // fall through
- case R.id.appmanager_app_delete:
- GBApplication.deviceService().onAppDelete(selectedApp.getUUID());
- return true;
- case R.id.appmanager_app_reinstall:
- File cachePath;
- try {
- cachePath = new File(FileUtils.getExternalFilesDir().getPath() + "/pbw-cache/" + selectedApp.getUUID() + ".pbw");
- } catch (IOException e) {
- LOG.warn("could not get external dir while trying to access pbw cache.");
- return true;
- }
- GBApplication.deviceService().onInstallApp(Uri.fromFile(cachePath));
- return true;
- case R.id.appmanager_health_activate:
- GBApplication.deviceService().onInstallApp(Uri.parse("fake://health"));
- return true;
- case R.id.appmanager_app_configure:
- GBApplication.deviceService().onAppStart(selectedApp.getUUID(), true);
-
- Intent startIntent = new Intent(getApplicationContext(), ExternalPebbleJSActivity.class);
- startIntent.putExtra(DeviceService.EXTRA_APP_UUID, selectedApp.getUUID());
- startIntent.putExtra(GBDevice.EXTRA_DEVICE, mGBDevice);
- startActivity(startIntent);
- return true;
- case R.id.appmanager_app_move_to_top:
- GBApplication.deviceService().onAppReorder(new UUID[]{selectedApp.getUUID()});
- return true;
- default:
- return super.onContextItemSelected(item);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- NavUtils.navigateUpFromSameTask(this);
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- protected void onDestroy() {
- LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
- super.onDestroy();
- }
-}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java
new file mode 100644
index 000000000..f96615db1
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java
@@ -0,0 +1,354 @@
+package nodomain.freeyourgadget.gadgetbridge.activities.appmanager;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.content.LocalBroadcastManager;
+import android.support.v7.widget.LinearLayoutManager;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.PopupMenu;
+
+import com.woxthebox.draglistview.DragListView;
+
+import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.UUID;
+
+import nodomain.freeyourgadget.gadgetbridge.GBApplication;
+import nodomain.freeyourgadget.gadgetbridge.R;
+import nodomain.freeyourgadget.gadgetbridge.activities.ExternalPebbleJSActivity;
+import nodomain.freeyourgadget.gadgetbridge.adapter.GBDeviceAppAdapter;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
+import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleProtocol;
+import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
+import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils;
+import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
+
+
+public abstract class AbstractAppManagerFragment extends Fragment {
+ public static final String ACTION_REFRESH_APPLIST
+ = "nodomain.freeyourgadget.gadgetbridge.appmanager.action.refresh_applist";
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractAppManagerFragment.class);
+
+
+ protected void refreshList() {
+ }
+
+ protected String getSortFilename() {
+ return null;
+ }
+
+ protected void onChangedAppOrder() {
+ List uuidList = new ArrayList<>();
+ for (GBDeviceApp gbDeviceApp : mGBDeviceAppAdapter.getItemList()) {
+ uuidList.add(gbDeviceApp.getUUID());
+ }
+ AppManagerActivity.rewriteAppOrderFile(getSortFilename(), uuidList);
+ }
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(GBApplication.ACTION_QUIT)) {
+ // finish();
+ } else if (action.equals(ACTION_REFRESH_APPLIST)) {
+ if (intent.hasExtra("app_count")) {
+ int appCount = intent.getIntExtra("app_count", 0);
+ for (Integer i = 0; i < appCount; i++) {
+ String appName = intent.getStringExtra("app_name" + i.toString());
+ String appCreator = intent.getStringExtra("app_creator" + i.toString());
+ UUID uuid = UUID.fromString(intent.getStringExtra("app_uuid" + i.toString()));
+ GBDeviceApp.Type appType = GBDeviceApp.Type.values()[intent.getIntExtra("app_type" + i.toString(), 0)];
+
+ boolean found = false;
+ for (final ListIterator iter = appList.listIterator(); iter.hasNext(); ) {
+ final GBDeviceApp app = iter.next();
+ if (app.getUUID().equals(uuid)) {
+ app.setOnDevice(true);
+ iter.set(app);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ GBDeviceApp app = new GBDeviceApp(uuid, appName, appCreator, "", appType);
+ app.setOnDevice(true);
+ appList.add(app);
+ }
+ }
+ } else {
+ refreshList();
+ }
+ mGBDeviceAppAdapter.notifyDataSetChanged();
+ }
+ }
+ };
+
+ private Prefs prefs;
+
+ protected final List appList = new ArrayList<>();
+ private GBDeviceAppAdapter mGBDeviceAppAdapter;
+ protected GBDevice mGBDevice = null;
+
+ protected List getSystemApps() {
+ List systemApps = new ArrayList<>();
+ if (prefs.getBoolean("pebble_force_untested", false)) {
+ systemApps.add(new GBDeviceApp(UUID.fromString("4dab81a6-d2fc-458a-992c-7a1f3b96a970"), "Sports (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
+ systemApps.add(new GBDeviceApp(UUID.fromString("cf1e816a-9db0-4511-bbb8-f60c48ca8fac"), "Golf (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
+ }
+ if (mGBDevice != null && !"aplite".equals(PebbleUtils.getPlatformName(mGBDevice.getHardwareVersion()))) {
+ systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
+ }
+
+ return systemApps;
+ }
+
+ protected List getSystemWatchfaces() {
+ List systemWatchfaces = new ArrayList<>();
+ systemWatchfaces.add(new GBDeviceApp(UUID.fromString("8f3c8686-31a1-4f5f-91f5-01600c9bdc59"), "Tic Toc (System)", "Pebble Inc.", "", GBDeviceApp.Type.WATCHFACE_SYSTEM));
+ return systemWatchfaces;
+ }
+
+ protected List getCachedApps(List uuids) {
+ List cachedAppList = new ArrayList<>();
+ File cachePath;
+ try {
+ cachePath = new File(FileUtils.getExternalFilesDir().getPath() + "/pbw-cache");
+ } catch (IOException e) {
+ LOG.warn("could not get external dir while reading pbw cache.");
+ return cachedAppList;
+ }
+
+ File[] files;
+ if (uuids == null) {
+ files = cachePath.listFiles();
+ } else {
+ files = new File[uuids.size()];
+ int index = 0;
+ for (UUID uuid : uuids) {
+ files[index++] = new File(uuid.toString() + ".pbw");
+ }
+ }
+ if (files != null) {
+ for (File file : files) {
+ if (file.getName().endsWith(".pbw")) {
+ String baseName = file.getName().substring(0, file.getName().length() - 4);
+ //metadata
+ File jsonFile = new File(cachePath, baseName + ".json");
+ //configuration
+ File configFile = new File(cachePath, baseName + "_config.js");
+ try {
+ String jsonstring = FileUtils.getStringFromFile(jsonFile);
+ JSONObject json = new JSONObject(jsonstring);
+ cachedAppList.add(new GBDeviceApp(json, configFile.exists()));
+ } catch (Exception e) {
+ LOG.info("could not read json file for " + baseName);
+ //FIXME: this is really ugly, if we do not find system uuids in pbw cache add them manually
+ if (prefs.getBoolean("pebble_force_untested", false)) {
+ if (baseName.equals("4dab81a6-d2fc-458a-992c-7a1f3b96a970")) {
+ cachedAppList.add(new GBDeviceApp(UUID.fromString("4dab81a6-d2fc-458a-992c-7a1f3b96a970"), "Sports (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
+ } else if (baseName.equals("cf1e816a-9db0-4511-bbb8-f60c48ca8fac")) {
+ cachedAppList.add(new GBDeviceApp(UUID.fromString("cf1e816a-9db0-4511-bbb8-f60c48ca8fac"), "Golf (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
+ }
+ }
+ if (baseName.equals("8f3c8686-31a1-4f5f-91f5-01600c9bdc59")) {
+ cachedAppList.add(new GBDeviceApp(UUID.fromString("8f3c8686-31a1-4f5f-91f5-01600c9bdc59"), "Tic Toc (System)", "Pebble Inc.", "", GBDeviceApp.Type.WATCHFACE_SYSTEM));
+ }
+ if (mGBDevice != null && !"aplite".equals(PebbleUtils.getPlatformName(mGBDevice.getHardwareVersion()))) {
+ if (baseName.equals(PebbleProtocol.UUID_PEBBLE_HEALTH.toString())) {
+ cachedAppList.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
+ continue;
+ }
+ }
+ if (uuids == null) {
+ cachedAppList.add(new GBDeviceApp(UUID.fromString(baseName), baseName, "N/A", "", GBDeviceApp.Type.UNKNOWN));
+ }
+ }
+ }
+ }
+ }
+ return cachedAppList;
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ mGBDevice = ((AppManagerActivity) getActivity()).getGBDevice();
+
+ prefs = GBApplication.getPrefs();
+
+ refreshList();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(GBApplication.ACTION_QUIT);
+ filter.addAction(ACTION_REFRESH_APPLIST);
+
+ LocalBroadcastManager.getInstance(getContext()).registerReceiver(mReceiver, filter);
+
+ GBApplication.deviceService().onAppInfoReq();
+
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+ View rootView = inflater.inflate(R.layout.activity_appmanager, container, false);
+
+ DragListView appListView = (DragListView) (rootView.findViewById(R.id.appListView));
+ appListView.setLayoutManager(new LinearLayoutManager(getActivity()));
+ mGBDeviceAppAdapter = new GBDeviceAppAdapter(appList, R.layout.item_with_details, R.id.item_image, this.getContext(), this);
+ appListView.setAdapter(mGBDeviceAppAdapter, false);
+ appListView.setCanDragHorizontally(false);
+ appListView.setDragListListener(new DragListView.DragListListener() {
+ @Override
+ public void onItemDragStarted(int position) {
+ }
+
+ @Override
+ public void onItemDragging(int itemPosition, float x, float y) {
+ }
+
+ @Override
+ public void onItemDragEnded(int fromPosition, int toPosition) {
+ onChangedAppOrder();
+ }
+ });
+ return rootView;
+ }
+
+ protected void sendOrderToDevice(String concatFilename) {
+ ArrayList uuids = new ArrayList();
+ for (GBDeviceApp gbDeviceApp : mGBDeviceAppAdapter.getItemList()) {
+ uuids.add(gbDeviceApp.getUUID());
+ }
+ if (concatFilename != null) {
+ ArrayList concatUuids = AppManagerActivity.getUuidsFromFile(concatFilename);
+ uuids.addAll(concatUuids);
+ }
+ GBApplication.deviceService().onAppReorder(uuids.toArray(new UUID[uuids.size()]));
+ }
+
+ private void removeAppFromList(UUID uuid) {
+ for (final ListIterator iter = appList.listIterator(); iter.hasNext(); ) {
+ final GBDeviceApp app = iter.next();
+ if (app.getUUID().equals(uuid)) {
+ iter.remove();
+ mGBDeviceAppAdapter.notifyDataSetChanged();
+ break;
+ }
+ }
+ }
+
+ public boolean openPopupMenu(View view, int position) {
+ PopupMenu popupMenu = new PopupMenu(getContext(), view);
+ popupMenu.getMenuInflater().inflate(R.menu.appmanager_context, popupMenu.getMenu());
+ Menu menu = popupMenu.getMenu();
+ final GBDeviceApp selectedApp = appList.get(position);
+
+ if (!selectedApp.isInCache()) {
+ menu.removeItem(R.id.appmanager_app_reinstall);
+ menu.removeItem(R.id.appmanager_app_delete_cache);
+ }
+ if (!PebbleProtocol.UUID_PEBBLE_HEALTH.equals(selectedApp.getUUID())) {
+ menu.removeItem(R.id.appmanager_health_activate);
+ menu.removeItem(R.id.appmanager_health_deactivate);
+ }
+ if (selectedApp.getType() == GBDeviceApp.Type.APP_SYSTEM) {
+ menu.removeItem(R.id.appmanager_app_delete);
+ }
+ if (!selectedApp.isConfigurable()) {
+ menu.removeItem(R.id.appmanager_app_configure);
+ }
+ //menu.setHeaderTitle(selectedApp.getName());
+ popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ return onContextItemSelected(item, selectedApp);
+ }
+ }
+ );
+ popupMenu.show();
+ return false; // FIXME: whats that for?
+ }
+
+ public boolean onContextItemSelected(MenuItem item, GBDeviceApp selectedApp) {
+ switch (item.getItemId()) {
+ case R.id.appmanager_health_deactivate:
+ case R.id.appmanager_app_delete_cache:
+ String baseName;
+ try {
+ baseName = FileUtils.getExternalFilesDir().getPath() + "/pbw-cache/" + selectedApp.getUUID();
+ } catch (IOException e) {
+ LOG.warn("could not get external dir while trying to access pbw cache.");
+ return true;
+ }
+
+ String[] suffixToDelete = new String[]{".pbw", ".json", "_config.js"};
+
+ for (String suffix : suffixToDelete) {
+ File fileToDelete = new File(baseName + suffix);
+ if (!fileToDelete.delete()) {
+ LOG.warn("could not delete file from pbw cache: " + fileToDelete.toString());
+ } else {
+ LOG.info("deleted file: " + fileToDelete.toString());
+ }
+ }
+ AppManagerActivity.deleteFromAppOrderFile("pbwcacheorder.txt", selectedApp.getUUID()); // FIXME: only if successful
+ // fall through
+ case R.id.appmanager_app_delete:
+ AppManagerActivity.deleteFromAppOrderFile(mGBDevice.getAddress() + ".watchapps", selectedApp.getUUID()); // FIXME: only if successful
+ AppManagerActivity.deleteFromAppOrderFile(mGBDevice.getAddress() + ".watchfaces", selectedApp.getUUID()); // FIXME: only if successful
+ Intent refreshIntent = new Intent(AbstractAppManagerFragment.ACTION_REFRESH_APPLIST);
+ LocalBroadcastManager.getInstance(getContext()).sendBroadcast(refreshIntent);
+ GBApplication.deviceService().onAppDelete(selectedApp.getUUID());
+ return true;
+ case R.id.appmanager_app_reinstall:
+ File cachePath;
+ try {
+ cachePath = new File(FileUtils.getExternalFilesDir().getPath() + "/pbw-cache/" + selectedApp.getUUID() + ".pbw");
+ } catch (IOException e) {
+ LOG.warn("could not get external dir while trying to access pbw cache.");
+ return true;
+ }
+ GBApplication.deviceService().onInstallApp(Uri.fromFile(cachePath));
+ return true;
+ case R.id.appmanager_health_activate:
+ GBApplication.deviceService().onInstallApp(Uri.parse("fake://health"));
+ return true;
+ case R.id.appmanager_app_configure:
+ GBApplication.deviceService().onAppStart(selectedApp.getUUID(), true);
+
+ Intent startIntent = new Intent(getContext().getApplicationContext(), ExternalPebbleJSActivity.class);
+ startIntent.putExtra(DeviceService.EXTRA_APP_UUID, selectedApp.getUUID());
+ startIntent.putExtra(GBDevice.EXTRA_DEVICE, mGBDevice);
+ startActivity(startIntent);
+ return true;
+ default:
+ return super.onContextItemSelected(item);
+ }
+ }
+
+
+ @Override
+ public void onDestroy() {
+ LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(mReceiver);
+ super.onDestroy();
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java
new file mode 100644
index 000000000..e1a9d44bf
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java
@@ -0,0 +1,156 @@
+package nodomain.freeyourgadget.gadgetbridge.activities.appmanager;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.NavUtils;
+import android.support.v4.view.ViewPager;
+import android.view.MenuItem;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import nodomain.freeyourgadget.gadgetbridge.R;
+import nodomain.freeyourgadget.gadgetbridge.activities.AbstractFragmentPagerAdapter;
+import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBFragmentActivity;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
+import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
+
+
+public class AppManagerActivity extends AbstractGBFragmentActivity {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractAppManagerFragment.class);
+
+ private GBDevice mGBDevice = null;
+
+ public GBDevice getGBDevice() {
+ return mGBDevice;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_fragmentappmanager);
+
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ mGBDevice = extras.getParcelable(GBDevice.EXTRA_DEVICE);
+ } else {
+ throw new IllegalArgumentException("Must provide a device when invoking this activity");
+ }
+
+
+ // Set up the ViewPager with the sections adapter.
+ ViewPager viewPager = (ViewPager) findViewById(R.id.appmanager_pager);
+ viewPager.setAdapter(getPagerAdapter());
+ }
+
+ @Override
+ protected AbstractFragmentPagerAdapter createFragmentPagerAdapter(FragmentManager fragmentManager) {
+ return new SectionsPagerAdapter(fragmentManager);
+ }
+
+ public static synchronized void deleteFromAppOrderFile(String filename, UUID uuid) {
+ ArrayList uuids = getUuidsFromFile(filename);
+ uuids.remove(uuid);
+ rewriteAppOrderFile(filename, uuids);
+ }
+
+ public class SectionsPagerAdapter extends AbstractFragmentPagerAdapter {
+
+ public SectionsPagerAdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ // getItem is called to instantiate the fragment for the given page.
+ switch (position) {
+ case 0:
+ return new AppManagerFragmentCache();
+ case 1:
+ return new AppManagerFragmentInstalledApps();
+ case 2:
+ return new AppManagerFragmentInstalledWatchfaces();
+ }
+ return null;
+ }
+
+ @Override
+ public int getCount() {
+ return 3;
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ switch (position) {
+ case 0:
+ return "Apps in cache";
+ case 1:
+ return "Installed apps";
+ case 2:
+ return "Installed watchfaces";
+ case 3:
+ }
+ return super.getPageTitle(position);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ NavUtils.navigateUpFromSameTask(this);
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+
+ static synchronized void rewriteAppOrderFile(String filename, List uuids) {
+ try {
+ FileWriter fileWriter = new FileWriter(FileUtils.getExternalFilesDir() + "/" + filename);
+ BufferedWriter out = new BufferedWriter(fileWriter);
+ for (UUID uuid : uuids) {
+ out.write(uuid.toString());
+ out.newLine();
+ }
+ out.close();
+ } catch (IOException e) {
+ LOG.warn("can't write app order to file!");
+ }
+ }
+
+ synchronized public static void addToAppOrderFile(String filename, UUID uuid) {
+ ArrayList uuids = getUuidsFromFile(filename);
+ uuids.remove(uuid); // if alread there
+ uuids.add(uuid);
+ rewriteAppOrderFile(filename, uuids);
+ }
+
+ static synchronized ArrayList getUuidsFromFile(String filename) {
+ ArrayList uuids = new ArrayList<>();
+ try {
+ FileReader fileReader = new FileReader(FileUtils.getExternalFilesDir() + "/" + filename);
+ BufferedReader in = new BufferedReader(fileReader);
+ String line;
+ while ((line = in.readLine()) != null) {
+ uuids.add(UUID.fromString(line));
+ }
+ } catch (IOException e) {
+ LOG.warn("could not read sort file");
+ }
+ return uuids;
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentCache.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentCache.java
new file mode 100644
index 000000000..6435bc125
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentCache.java
@@ -0,0 +1,14 @@
+package nodomain.freeyourgadget.gadgetbridge.activities.appmanager;
+
+public class AppManagerFragmentCache extends AbstractAppManagerFragment {
+ @Override
+ public void refreshList() {
+ appList.clear();
+ appList.addAll(getCachedApps(null));
+ }
+
+ @Override
+ public String getSortFilename() {
+ return "pbwcacheorder.txt";
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java
new file mode 100644
index 000000000..474e4c24d
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java
@@ -0,0 +1,33 @@
+package nodomain.freeyourgadget.gadgetbridge.activities.appmanager;
+
+import java.util.ArrayList;
+
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
+
+public class AppManagerFragmentInstalledApps extends AbstractAppManagerFragment {
+ @Override
+ protected void refreshList() {
+ appList.clear();
+ ArrayList uuids = AppManagerActivity.getUuidsFromFile(getSortFilename());
+ if (uuids.isEmpty()) {
+ appList.addAll(getSystemApps());
+ for (GBDeviceApp gbDeviceApp : appList) {
+ uuids.add(gbDeviceApp.getUUID());
+ }
+ AppManagerActivity.rewriteAppOrderFile(getSortFilename(), uuids);
+ } else {
+ appList.addAll(getCachedApps(uuids));
+ }
+ }
+
+ @Override
+ protected String getSortFilename() {
+ return mGBDevice.getAddress() + ".watchapps";
+ }
+
+ @Override
+ protected void onChangedAppOrder() {
+ super.onChangedAppOrder();
+ sendOrderToDevice(mGBDevice.getAddress() + ".watchfaces");
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java
new file mode 100644
index 000000000..799584591
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java
@@ -0,0 +1,33 @@
+package nodomain.freeyourgadget.gadgetbridge.activities.appmanager;
+
+import java.util.ArrayList;
+
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
+
+public class AppManagerFragmentInstalledWatchfaces extends AbstractAppManagerFragment {
+ @Override
+ protected void refreshList() {
+ appList.clear();
+ ArrayList uuids = AppManagerActivity.getUuidsFromFile(getSortFilename());
+ if (uuids.isEmpty()) {
+ appList.addAll(getSystemWatchfaces());
+ for (GBDeviceApp gbDeviceApp : appList) {
+ uuids.add(gbDeviceApp.getUUID());
+ }
+ AppManagerActivity.rewriteAppOrderFile(getSortFilename(), uuids);
+ } else {
+ appList.addAll(getCachedApps(uuids));
+ }
+ }
+
+ @Override
+ protected String getSortFilename() {
+ return mGBDevice.getAddress() + ".watchfaces";
+ }
+
+ @Override
+ protected void onChangedAppOrder() {
+ super.onChangedAppOrder();
+ sendOrderToDevice(mGBDevice.getAddress() + ".watchapps");
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAppAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAppAdapter.java
index d4ae263f3..35549ae80 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAppAdapter.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAppAdapter.java
@@ -4,69 +4,102 @@ import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
-import java.util.List;
+import com.woxthebox.draglistview.DragItemAdapter;
+import java.util.List;
+import java.util.UUID;
+
+import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
+import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AbstractAppManagerFragment;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
/**
* Adapter for displaying GBDeviceApp instances.
*/
-public class GBDeviceAppAdapter extends ArrayAdapter {
- private final Context context;
+public class GBDeviceAppAdapter extends DragItemAdapter {
- public GBDeviceAppAdapter(Context context, List appList) {
- super(context, 0, appList);
+ private final int mLayoutId;
+ private final int mGrabHandleId;
+ private final Context mContext;
+ private final AbstractAppManagerFragment mParentFragment;
- this.context = context;
+ public GBDeviceAppAdapter(List list, int layoutId, int grabHandleId, Context context, AbstractAppManagerFragment parentFragment) {
+ super(true); // longpress
+ mLayoutId = layoutId;
+ mGrabHandleId = grabHandleId;
+ mContext = context;
+ mParentFragment = parentFragment;
+ setHasStableIds(true);
+ setItemList(list);
}
@Override
- public View getView(int position, View view, ViewGroup parent) {
- GBDeviceApp deviceApp = getItem(position);
+ public long getItemId(int position) {
+ return mItemList.get(position).getUUID().getLeastSignificantBits();
+ }
- if (view == null) {
- LayoutInflater inflater = (LayoutInflater) context
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- view = inflater.inflate(R.layout.item_with_details, parent, false);
- }
- TextView deviceAppVersionAuthorLabel = (TextView) view.findViewById(R.id.item_details);
- TextView deviceAppNameLabel = (TextView) view.findViewById(R.id.item_name);
- ImageView deviceImageView = (ImageView) view.findViewById(R.id.item_image);
+ View view = LayoutInflater.from(parent.getContext()).inflate(mLayoutId, parent, false);
+ return new ViewHolder(view);
+ }
- deviceAppVersionAuthorLabel.setText(getContext().getString(R.string.appversion_by_creator, deviceApp.getVersion(), deviceApp.getCreator()));
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position) {
+ super.onBindViewHolder(holder, position);
+ GBDeviceApp deviceApp = mItemList.get(position);
+
+ holder.mDeviceAppVersionAuthorLabel.setText(GBApplication.getContext().getString(R.string.appversion_by_creator, deviceApp.getVersion(), deviceApp.getCreator()));
// FIXME: replace with small icons
String appNameLabelText = deviceApp.getName();
- if (deviceApp.isInCache() || deviceApp.isOnDevice()) {
- appNameLabelText += " (" + (deviceApp.isInCache() ? "C" : "")
- + (deviceApp.isOnDevice() ? "D" : "") + ")";
- }
- deviceAppNameLabel.setText(appNameLabelText);
+ holder.mDeviceAppNameLabel.setText(appNameLabelText);
switch (deviceApp.getType()) {
case APP_GENERIC:
- deviceImageView.setImageResource(R.drawable.ic_watchapp);
+ holder.mDeviceImageView.setImageResource(R.drawable.ic_watchapp);
break;
case APP_ACTIVITYTRACKER:
- deviceImageView.setImageResource(R.drawable.ic_activitytracker);
+ holder.mDeviceImageView.setImageResource(R.drawable.ic_activitytracker);
break;
case APP_SYSTEM:
- deviceImageView.setImageResource(R.drawable.ic_systemapp);
+ holder.mDeviceImageView.setImageResource(R.drawable.ic_systemapp);
break;
case WATCHFACE:
- deviceImageView.setImageResource(R.drawable.ic_watchface);
+ holder.mDeviceImageView.setImageResource(R.drawable.ic_watchface);
break;
default:
- deviceImageView.setImageResource(R.drawable.ic_watchapp);
+ holder.mDeviceImageView.setImageResource(R.drawable.ic_watchapp);
+ }
+ }
+
+ public class ViewHolder extends DragItemAdapter.ViewHolder {
+ TextView mDeviceAppVersionAuthorLabel;
+ TextView mDeviceAppNameLabel;
+ ImageView mDeviceImageView;
+
+ public ViewHolder(final View itemView) {
+ super(itemView, mGrabHandleId);
+ mDeviceAppVersionAuthorLabel = (TextView) itemView.findViewById(R.id.item_details);
+ mDeviceAppNameLabel = (TextView) itemView.findViewById(R.id.item_name);
+ mDeviceImageView = (ImageView) itemView.findViewById(R.id.item_image);
}
- return view;
+ @Override
+ public void onItemClicked(View view) {
+ UUID uuid = mItemList.get(getAdapterPosition()).getUUID();
+ GBApplication.deviceService().onAppStart(uuid, true);
+ }
+
+ @Override
+ public boolean onItemLongClicked(View view) {
+ return mParentFragment.openPopupMenu(view, getAdapterPosition());
+ }
}
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java
index 9d66c0dbc..a00f49b2d 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java
@@ -18,6 +18,7 @@ import java.io.Writer;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
+import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AppManagerActivity;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
@@ -135,6 +136,8 @@ public class PBWInstallHandler implements InstallHandler {
destDir = new File(FileUtils.getExternalFilesDir() + "/pbw-cache");
destDir.mkdirs();
FileUtils.copyURItoFile(mContext, mUri, new File(destDir, app.getUUID().toString() + ".pbw"));
+
+ AppManagerActivity.addToAppOrderFile("pbwcacheorder.txt", app.getUUID());
} catch (IOException e) {
LOG.error("Installation failed: " + e.getMessage(), e);
return;
@@ -174,6 +177,7 @@ public class PBWInstallHandler implements InstallHandler {
LOG.error("Failed to open output file: " + e.getMessage(), e);
}
}
+
}
public boolean isValid() {
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java
index 6ada2cd7d..99b5e1557 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java
@@ -6,17 +6,14 @@ import android.net.Uri;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
-import nodomain.freeyourgadget.gadgetbridge.activities.AppManagerActivity;
-import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
+import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AppManagerActivity;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
-import nodomain.freeyourgadget.gadgetbridge.entities.PebbleActivitySample;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
-import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceApp.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceApp.java
index f55876453..5cbd0fab0 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceApp.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceApp.java
@@ -89,6 +89,7 @@ public class GBDeviceApp {
public enum Type {
UNKNOWN,
WATCHFACE,
+ WATCHFACE_SYSTEM,
APP_GENERIC,
APP_ACTIVITYTRACKER,
APP_SYSTEM,
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java
index 21d7965b9..627a1cf51 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java
@@ -24,7 +24,7 @@ import java.util.Objects;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
-import nodomain.freeyourgadget.gadgetbridge.activities.AppManagerActivity;
+import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AbstractAppManagerFragment;
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsHost;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
@@ -160,7 +160,7 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
Context context = getContext();
LOG.info("Got event for APP_INFO");
- Intent appInfoIntent = new Intent(AppManagerActivity.ACTION_REFRESH_APPLIST);
+ Intent appInfoIntent = new Intent(AbstractAppManagerFragment.ACTION_REFRESH_APPLIST);
int appCount = appInfoEvent.apps.length;
appInfoIntent.putExtra("app_count", appCount);
for (Integer i = 0; i < appCount; i++) {
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java
index e7fd0d7e7..cb0764711 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java
@@ -9,6 +9,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.ParcelUuid;
+import android.support.v4.content.LocalBroadcastManager;
import org.json.JSONArray;
import org.json.JSONException;
@@ -28,6 +29,8 @@ import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
+import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AbstractAppManagerFragment;
+import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AppManagerActivity;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppManagement;
@@ -75,6 +78,7 @@ public class PebbleIoThread extends GBDeviceIoThread {
private boolean mIsInstalling = false;
private PBWReader mPBWReader = null;
+ private GBDeviceApp mCurrentlyInstallingApp = null;
private int mAppInstallToken = -1;
private InputStream mFis = null;
private PebbleAppInstallState mInstallState = PebbleAppInstallState.UNKNOWN;
@@ -613,12 +617,12 @@ public class PebbleIoThread extends GBDeviceIoThread {
*/
writeInstallApp(mPebbleProtocol.encodeGetTime());
} else {
- GBDeviceApp app = mPBWReader.getGBDeviceApp();
+ mCurrentlyInstallingApp = mPBWReader.getGBDeviceApp();
if (mPebbleProtocol.mFwMajor >= 3 && !mPBWReader.isLanguage()) {
if (appId == 0) {
// only install metadata - not the binaries
- write(mPebbleProtocol.encodeInstallMetadata(app.getUUID(), app.getName(), mPBWReader.getAppVersion(), mPBWReader.getSdkVersion(), mPBWReader.getFlags(), mPBWReader.getIconId()));
- write(mPebbleProtocol.encodeAppStart(app.getUUID(), true));
+ write(mPebbleProtocol.encodeInstallMetadata(mCurrentlyInstallingApp.getUUID(), mCurrentlyInstallingApp.getName(), mPBWReader.getAppVersion(), mPBWReader.getSdkVersion(), mPBWReader.getFlags(), mPBWReader.getIconId()));
+ write(mPebbleProtocol.encodeAppStart(mCurrentlyInstallingApp.getUUID(), true));
} else {
// this came from an app fetch request, so do the real stuff
mIsInstalling = true;
@@ -637,7 +641,7 @@ public class PebbleIoThread extends GBDeviceIoThread {
writeInstallApp(mPebbleProtocol.encodeGetTime());
} else {
mInstallState = PebbleAppInstallState.WAIT_SLOT;
- writeInstallApp(mPebbleProtocol.encodeAppDelete(app.getUUID()));
+ writeInstallApp(mPebbleProtocol.encodeAppDelete(mCurrentlyInstallingApp.getUUID()));
}
}
}
@@ -651,6 +655,17 @@ public class PebbleIoThread extends GBDeviceIoThread {
GB.updateInstallNotification(getContext().getString(R.string.installation_failed_), false, 0, getContext());
} else {
GB.updateInstallNotification(getContext().getString(R.string.installation_successful), false, 0, getContext());
+ String filenameSuffix;
+ if (mCurrentlyInstallingApp != null) {
+ if (mCurrentlyInstallingApp.getType() == GBDeviceApp.Type.WATCHFACE) {
+ filenameSuffix = ".watchfaces";
+ } else {
+ filenameSuffix = ".watchapps";
+ }
+ AppManagerActivity.addToAppOrderFile(gbDevice.getAddress() + filenameSuffix, mCurrentlyInstallingApp.getUUID());
+ Intent refreshIntent = new Intent(AbstractAppManagerFragment.ACTION_REFRESH_APPLIST);
+ LocalBroadcastManager.getInstance(getContext()).sendBroadcast(refreshIntent);
+ }
}
mInstallState = PebbleAppInstallState.UNKNOWN;
@@ -660,6 +675,8 @@ public class PebbleIoThread extends GBDeviceIoThread {
mPBWReader = null;
mIsInstalling = false;
+ mCurrentlyInstallingApp = null;
+
if (mFis != null) {
try {
mFis.close();
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java
index 262d1a4a9..b209d24d1 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java
@@ -1109,6 +1109,10 @@ public class PebbleProtocol extends GBDeviceProtocol {
}
public byte[] encodeSetMusicState(byte state, int position, int playRate, byte shuffle, byte repeat) {
+ if (mFwMajor < 3) {
+ return null;
+ }
+
byte playState;
switch (state) {
@@ -1144,7 +1148,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
@Override
public byte[] encodeSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) {
String[] parts = {artist, album, track};
- if (duration == 0) {
+ if (duration == 0 || mFwMajor < 3) {
return encodeMessage(ENDPOINT_MUSICCONTROL, MUSICCONTROL_SETMUSICINFO, 0, parts);
} else {
// Calculate length first
diff --git a/app/src/main/res/layout/activity_appmanager.xml b/app/src/main/res/layout/activity_appmanager.xml
index 868f628fa..bdc6317c3 100644
--- a/app/src/main/res/layout/activity_appmanager.xml
+++ b/app/src/main/res/layout/activity_appmanager.xml
@@ -1,16 +1,17 @@
-
+
-
+
-
+
+
+
+
diff --git a/app/src/main/res/layout/activity_fragmentappmanager.xml b/app/src/main/res/layout/activity_fragmentappmanager.xml
new file mode 100644
index 000000000..639a58f72
--- /dev/null
+++ b/app/src/main/res/layout/activity_fragmentappmanager.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/appmanager_context.xml b/app/src/main/res/menu/appmanager_context.xml
index 72cb29300..873e0c246 100644
--- a/app/src/main/res/menu/appmanager_context.xml
+++ b/app/src/main/res/menu/appmanager_context.xml
@@ -18,8 +18,4 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml
index 089322bed..31efd8ece 100644
--- a/app/src/main/res/xml/changelog_master.xml
+++ b/app/src/main/res/xml/changelog_master.xml
@@ -1,11 +1,10 @@
-
- Pebble: allow to manually paste configuration data for legacy configuration pages
-
+
+ Pebble: allow to manually paste configuration data for legacy configuration pages
Pebble: various improvements to the configuration page
+ Pebble: Suppport FW 4.0-dp1 and Pebble2 emulator (needs recompilation of Gadgetbridge)
+ Pebble: Fix a problem with key events when using the Pebble music player
Pebble: set extended music info by dissecting notifications on Android 5.0+