add Activity to choose which installed apps are in the local repo

This gets the data about which apps are installed from the ContentProvider
that pserwylo recently added for data about "Installed Apps" and presents
a list view for the user to select from by touching each line.  Then if the
user chooses "Update Repo", it will regenerate the local repo based on the
current selection.  It will always include FDroid in the local repo.

fixes #3232 https://dev.guardianproject.info/issues/3232
refs #3204 https://dev.guardianproject.info/issues/3204
This commit is contained in:
Hans-Christoph Steiner 2014-05-05 19:47:21 -04:00
parent 678e2b09ea
commit 08346b9b18
9 changed files with 272 additions and 6 deletions

View File

@ -197,6 +197,14 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".views.SelectLocalAppsActivity"
android:label="@string/setup_repo"
android:parentActivityName=".views.LocalRepoActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".views.LocalRepoActivity" />
</activity>
<activity
android:name=".views.RepoDetailsActivity"
android:label="@string/menu_manage"

View File

@ -0,0 +1,12 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment
android:id="@+id/fragment_app_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
class="org.fdroid.fdroid.views.fragments.SelectLocalAppsFragment" />
</RelativeLayout>

View File

@ -0,0 +1,9 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/action_update_repo"
android:icon="@android:drawable/ic_input_add"
android:showAsAction="ifRoom|withText"
android:title="@string/update_repo"/>
</menu>

View File

@ -0,0 +1,10 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:showAsAction="never"
android:icon="@android:drawable/ic_menu_preferences"
android:title="@string/menu_preferences"/>
</menu>

View File

@ -157,12 +157,15 @@
<string name="setup_repo">Setup Local Repo</string>
<string name="touch_to_configure_local_repo">Touch to setup your local repo.</string>
<string name="updating">Updating&#8230;</string>
<string name="update_repo">Update Repo</string>
<string name="deleting_repo">Deleting current repo&#8230;</string>
<string name="adding_apks_format">Adding %s to repo&#8230;</string>
<string name="writing_index_xml">Writing raw index file (index.xml)&#8230;</string>
<string name="linking_apks">Linking APKs into the repo&#8230;</string>
<string name="copying_icons">Copying app icons into the repo&#8230;</string>
<string name="updated_local_repo">Finished updating local repo</string>
<string name="no_applications_found">No applications found</string>
<string name="icon">icon</string>
<string name="fingerprint">Fingerprint:</string>
<string name="wifi_network">WiFi Network:</string>
<string name="enable_wifi">Enable WiFi</string>

View File

@ -85,7 +85,7 @@ public class FDroidApp extends Application {
public static String bssid = "";
public static Repo repo = new Repo();
public static LocalRepoManager localRepo = null;
static Set<String> selectedApps = new HashSet<String>();
public static Set<String> selectedApps = new HashSet<String>();
private static Messenger localRepoServiceMessenger = null;
private static boolean localRepoServiceIsBound = false;

View File

@ -36,6 +36,7 @@ public class LocalRepoActivity extends Activity {
private ToggleButton repoSwitch;
private int SET_IP_ADDRESS = 7345;
private int UPDATE_REPO = 7346;
/** Called when the activity is first created. */
@Override
@ -106,11 +107,7 @@ public class LocalRepoActivity extends Activity {
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_setup_repo:
setUIFromWifi();
String[] packages = new String[2];
packages[0] = getPackageName();
packages[1] = "com.android.bluetooth";
new UpdateAsyncTask(this, packages).execute();
startActivityForResult(new Intent(this, SelectLocalAppsActivity.class), UPDATE_REPO);
return true;
case R.id.menu_send_fdroid_via_wifi:
startActivity(new Intent(this, QrWizardWifiNetworkActivity.class));
@ -128,6 +125,10 @@ public class LocalRepoActivity extends Activity {
return;
if (requestCode == SET_IP_ADDRESS) {
setUIFromWifi();
} else if (requestCode == UPDATE_REPO) {
setUIFromWifi();
new UpdateAsyncTask(this, FDroidApp.selectedApps.toArray(new String[0]))
.execute();
}
}

View File

@ -0,0 +1,89 @@
package org.fdroid.fdroid.views;
import android.annotation.TargetApi;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import org.fdroid.fdroid.PreferencesActivity;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.views.fragments.SelectLocalAppsFragment;
@TargetApi(11)
// TODO replace with appcompat-v7
public class SelectLocalAppsActivity extends FragmentActivity {
private static final String TAG = "SelectLocalAppsActivity";
private SelectLocalAppsFragment selectLocalAppsFragment = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.select_local_apps_activity);
}
@Override
protected void onResume() {
super.onResume();
if (selectLocalAppsFragment == null)
selectLocalAppsFragment = (SelectLocalAppsFragment) getSupportFragmentManager().findFragmentById(
R.id.fragment_app_list);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.select_local_apps_activity, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
setResult(RESULT_CANCELED);
finish();
return true;
case R.id.action_settings:
startActivity(new Intent(this, PreferencesActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
public ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.select_local_apps_action_mode, menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false; // Return false if nothing is done
}
@Override
public boolean onActionItemClicked(final ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.action_update_repo:
setResult(RESULT_OK);
finish();
return true;
default:
return false;
}
}
@Override
public void onDestroyActionMode(ActionMode mode) {
setResult(RESULT_CANCELED);
finish();
}
};
}

View File

@ -0,0 +1,134 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package org.fdroid.fdroid.views.fragments;
import android.annotation.TargetApi;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.SimpleCursorAdapter;
import android.text.TextUtils;
import android.view.ActionMode;
import android.view.View;
import android.widget.ListView;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.data.InstalledAppProvider;
import org.fdroid.fdroid.data.InstalledAppProvider.DataColumns;
import org.fdroid.fdroid.views.SelectLocalAppsActivity;
public class SelectLocalAppsFragment extends ListFragment implements LoaderCallbacks<Cursor> {
private SelectLocalAppsActivity selectLocalAppsActivity;
private ActionMode mActionMode = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@TargetApi(11)
// TODO replace with appcompat-v7
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setEmptyText(getString(R.string.no_applications_found));
selectLocalAppsActivity = (SelectLocalAppsActivity) getActivity();
ListView listView = getListView();
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
SimpleCursorAdapter adapter = new SimpleCursorAdapter(getActivity(),
android.R.layout.simple_list_item_activated_1,
null,
new String[] {
InstalledAppProvider.DataColumns.APP_ID,
},
new int[] {
android.R.id.text1,
},
0);
setListAdapter(adapter);
setListShown(false);
// either reconnect with an existing loader or start a new one
getLoaderManager().initLoader(0, null, this);
}
@TargetApi(11)
// TODO replace with appcompat-v7
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
if (mActionMode == null)
mActionMode = selectLocalAppsActivity
.startActionMode(selectLocalAppsActivity.mActionModeCallback);
Cursor cursor = (Cursor) l.getAdapter().getItem(position);
String packageName = cursor.getString(1);
if (FDroidApp.selectedApps.contains(packageName)) {
FDroidApp.selectedApps.remove(packageName);
} else {
FDroidApp.selectedApps.add(packageName);
}
}
@Override
public CursorLoader onCreateLoader(int id, Bundle args) {
CursorLoader loader = new CursorLoader(
this.getActivity(),
InstalledAppProvider.getContentUri(),
InstalledAppProvider.DataColumns.ALL,
null,
null,
InstalledAppProvider.DataColumns.APP_ID);
return loader;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
((SimpleCursorAdapter) this.getListAdapter()).swapCursor(cursor);
ListView listView = getListView();
int count = listView.getCount();
String fdroid = loader.getContext().getPackageName();
for (int i = 0; i < count; i++) {
Cursor c = ((Cursor) listView.getItemAtPosition(i));
String packageName = c.getString(c.getColumnIndex(DataColumns.APP_ID));
if (TextUtils.equals(packageName, fdroid)) {
listView.setItemChecked(i, true);
} else {
for (String selected : FDroidApp.selectedApps) {
if (TextUtils.equals(packageName, selected)) {
listView.setItemChecked(i, true);
}
}
}
}
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
((SimpleCursorAdapter) this.getListAdapter()).swapCursor(null);
}
}