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 index e0e927dac..bc92624fd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java @@ -48,13 +48,21 @@ public abstract class AbstractAppManagerFragment extends Fragment { private static final Logger LOG = LoggerFactory.getLogger(AbstractAppManagerFragment.class); - public void refreshList() { + protected void refreshList() { } - public String getSortFilename() { + 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) { @@ -62,30 +70,33 @@ public abstract class AbstractAppManagerFragment extends Fragment { 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)]; + 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)) { + 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); - iter.set(app); - found = true; - break; + appList.add(app); } } - if (!found) { - GBDeviceApp app = new GBDeviceApp(uuid, appName, appCreator, "", appType); - app.setOnDevice(true); - appList.add(app); - } + } else { + refreshList(); } - mGBDeviceAppAdapter.notifyDataSetChanged(); } } @@ -116,7 +127,7 @@ public abstract class AbstractAppManagerFragment extends Fragment { return systemWatchfaces; } - protected List getCachedApps() { + protected List getCachedApps(List uuids) { List cachedAppList = new ArrayList<>(); File cachePath; try { @@ -126,7 +137,16 @@ public abstract class AbstractAppManagerFragment extends Fragment { return cachedAppList; } - File files[] = cachePath.listFiles(); + 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")) { @@ -140,8 +160,27 @@ public abstract class AbstractAppManagerFragment extends Fragment { 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)); + 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)); + } } } } @@ -177,9 +216,35 @@ public abstract class AbstractAppManagerFragment extends Fragment { 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(); @@ -247,9 +312,13 @@ public abstract class AbstractAppManagerFragment extends Fragment { LOG.info("deleted file: " + fileToDelete.toString()); } } - removeAppFromList(selectedApp.getUUID()); + 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: @@ -274,7 +343,6 @@ public abstract class AbstractAppManagerFragment extends Fragment { 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); 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 index d71ead93f..e1a9d44bf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java @@ -7,13 +7,29 @@ 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() { @@ -45,6 +61,12 @@ public class AppManagerActivity extends AbstractGBFragmentActivity { 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) { @@ -95,4 +117,40 @@ public class AppManagerActivity extends AbstractGBFragmentActivity { 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 index e902912a3..6435bc125 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentCache.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentCache.java @@ -3,6 +3,12 @@ package nodomain.freeyourgadget.gadgetbridge.activities.appmanager; public class AppManagerFragmentCache extends AbstractAppManagerFragment { @Override public void refreshList() { - appList.addAll(getCachedApps()); + 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 index bfc28e469..474e4c24d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java @@ -1,13 +1,33 @@ package nodomain.freeyourgadget.gadgetbridge.activities.appmanager; +import java.util.ArrayList; + +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; + public class AppManagerFragmentInstalledApps extends AbstractAppManagerFragment { @Override - public void refreshList() { - appList.addAll(getSystemApps()); + 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 - public String getSortFilename() { + @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 index 14c916f05..799584591 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java @@ -1,12 +1,33 @@ package nodomain.freeyourgadget.gadgetbridge.activities.appmanager; +import java.util.ArrayList; + +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; + public class AppManagerFragmentInstalledWatchfaces extends AbstractAppManagerFragment { @Override - public void refreshList() { - appList.addAll(getSystemWatchfaces()); + 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)); + } } - public String getSortFilename() { + @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 56a263a75..35549ae80 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAppAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAppAdapter.java @@ -59,10 +59,6 @@ public class GBDeviceAppAdapter extends DragItemAdapter= 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();