Removed old SwapActivity + Fragments.

All the code from the activity and the fragments has been successfully
ported to the SwapWorkflowActivity + Views. Thus, the code is no longer
useful, as it was only kept over the previous WIP commits so that it
can be referred to to help re-implement fragments with views.
This commit is contained in:
Peter Serwylo 2015-05-25 16:02:41 +10:00
parent f325e9d057
commit c26c6b9e89
21 changed files with 17 additions and 999 deletions

View File

@ -385,17 +385,6 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".FDroid" />
</activity>
<activity
android:label="@string/menu_swap"
android:name=".views.swap.SwapActivity"
android:parentActivityName=".FDroid"
android:theme="@style/SwapTheme.Wizard"
android:screenOrientation="portrait"
android:configChanges="orientation|keyboardHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".FDroid" />
</activity>
<activity
android:label="@string/menu_swap"
android:name=".views.swap.SwapWorkflowActivity"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<org.fdroid.fdroid.views.swap.views.StartSwapView
<org.fdroid.fdroid.views.swap.StartSwapView
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
@ -20,4 +20,4 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</org.fdroid.fdroid.views.swap.views.StartSwapView>
</org.fdroid.fdroid.views.swap.StartSwapView>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<org.fdroid.fdroid.views.swap.views.JoinWifiView xmlns:android="http://schemas.android.com/apk/res/android"
<org.fdroid.fdroid.views.swap.JoinWifiView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -59,4 +59,4 @@
android:paddingBottom="20dp"/>
<!-- android:layout_above="@id/btn_learn_more_about_wifi" -->
</org.fdroid.fdroid.views.swap.views.JoinWifiView>
</org.fdroid.fdroid.views.swap.JoinWifiView>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<org.fdroid.fdroid.views.swap.views.NfcView xmlns:android="http://schemas.android.com/apk/res/android"
<org.fdroid.fdroid.views.swap.NfcView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -32,4 +32,4 @@
android:layout_below="@+id/text_description"
android:layout_centerHorizontal="true"/>
</org.fdroid.fdroid.views.swap.views.NfcView>
</org.fdroid.fdroid.views.swap.NfcView>

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<org.fdroid.fdroid.views.swap.views.SelectAppsView
<org.fdroid.fdroid.views.swap.SelectAppsView
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
</org.fdroid.fdroid.views.swap.views.SelectAppsView>
</org.fdroid.fdroid.views.swap.SelectAppsView>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<org.fdroid.fdroid.views.swap.views.WifiQrView
<org.fdroid.fdroid.views.swap.WifiQrView
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@color/swap_blue"
@ -51,4 +51,4 @@
</LinearLayout>
</org.fdroid.fdroid.views.swap.views.WifiQrView>
</org.fdroid.fdroid.views.swap.WifiQrView>

View File

@ -50,7 +50,6 @@ import org.fdroid.fdroid.data.NewRepoConfig;
import org.fdroid.fdroid.views.AppListFragmentPagerAdapter;
import org.fdroid.fdroid.views.ManageReposActivity;
import org.fdroid.fdroid.views.swap.ConnectSwapActivity;
import org.fdroid.fdroid.views.swap.SwapActivity;
import org.fdroid.fdroid.views.swap.SwapWorkflowActivity;
public class FDroid extends ActionBarActivity {

View File

@ -69,7 +69,6 @@ public class FDroidApp extends Application {
public static String ssid;
public static String bssid;
public static final Repo repo = new Repo();
public static Set<String> selectedApps = null; // init in SelectLocalAppsFragment
// Leaving the fully qualified class name here to help clarify the difference between spongy/bouncy castle.
private static final org.spongycastle.jce.provider.BouncyCastleProvider spongyCastleProvider;

View File

@ -26,7 +26,7 @@ import org.fdroid.fdroid.R;
import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.net.LocalHTTPD;
import org.fdroid.fdroid.net.WifiStateChangeService;
import org.fdroid.fdroid.views.swap.SwapActivity;
import org.fdroid.fdroid.views.swap.SwapWorkflowActivity;
import java.io.IOException;
import java.net.BindException;
@ -136,7 +136,7 @@ public class LocalRepoService extends Service {
private void showNotification() {
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// launch LocalRepoActivity if the user selects this notification
Intent intent = new Intent(this, SwapActivity.class);
Intent intent = new Intent(this, SwapWorkflowActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
notification = new NotificationCompat.Builder(this)

View File

@ -1,109 +0,0 @@
package org.fdroid.fdroid.views.swap;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.view.MenuItemCompat;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.net.WifiStateChangeService;
public class JoinWifiFragment extends Fragment {
private final BroadcastReceiver onWifiChange = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
refreshWifiState();
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
menuInflater.inflate(R.menu.swap_next, menu);
MenuItem nextMenuItem = menu.findItem(R.id.action_next);
int flags = MenuItemCompat.SHOW_AS_ACTION_ALWAYS | MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT;
MenuItemCompat.setShowAsAction(nextMenuItem, flags);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View joinWifiView = inflater.inflate(R.layout.swap_join_wifi, container, false);
joinWifiView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
openAvailableNetworks();
}
});
return joinWifiView;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// TODO: Listen for "Connecting..." state and reflect that in the view too.
LocalBroadcastManager.getInstance(activity).registerReceiver(
onWifiChange,
new IntentFilter(WifiStateChangeService.BROADCAST));
}
@Override
public void onResume() {
super.onResume();
refreshWifiState();
}
private void refreshWifiState() {
View view = getView();
if (view != null) {
TextView descriptionView = (TextView) view.findViewById(R.id.text_description);
ImageView wifiIcon = (ImageView) view.findViewById(R.id.wifi_icon);
TextView ssidView = (TextView) view.findViewById(R.id.wifi_ssid);
TextView tapView = (TextView) view.findViewById(R.id.wifi_available_networks_prompt);
if (TextUtils.isEmpty(FDroidApp.bssid) && !TextUtils.isEmpty(FDroidApp.ipAddressString)) {
// empty bssid with an ipAddress means hotspot mode
descriptionView.setText(R.string.swap_join_this_hotspot);
wifiIcon.setImageDrawable(getResources().getDrawable(R.drawable.hotspot));
ssidView.setText(R.string.swap_active_hotspot);
tapView.setText(R.string.swap_switch_to_wifi);
} else if (TextUtils.isEmpty(FDroidApp.ssid)) {
// not connected to or setup with any wifi network
descriptionView.setText(R.string.swap_join_same_wifi);
wifiIcon.setImageDrawable(getResources().getDrawable(R.drawable.wifi));
ssidView.setText(R.string.swap_no_wifi_network);
tapView.setText(R.string.swap_view_available_networks);
} else {
// connected to a regular wifi network
descriptionView.setText(R.string.swap_join_same_wifi);
wifiIcon.setImageDrawable(getResources().getDrawable(R.drawable.wifi));
ssidView.setText(FDroidApp.ssid);
tapView.setText(R.string.swap_view_available_networks);
}
}
}
private void openAvailableNetworks() {
startActivity(new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK));
}
}

View File

@ -1,4 +1,4 @@
package org.fdroid.fdroid.views.swap.views;
package org.fdroid.fdroid.views.swap;
import android.annotation.TargetApi;
import android.content.BroadcastReceiver;

View File

@ -1,47 +0,0 @@
package org.fdroid.fdroid.views.swap;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.R;
public class NfcSwapFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
menuInflater.inflate(R.menu.swap_skip, menu);
MenuItem nextMenuItem = menu.findItem(R.id.action_next);
int flags = MenuItemCompat.SHOW_AS_ACTION_ALWAYS | MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT;
MenuItemCompat.setShowAsAction(nextMenuItem, flags);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.swap_nfc, container, false);
CheckBox dontShowAgain = (CheckBox)view.findViewById(R.id.checkbox_dont_show);
dontShowAgain.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Preferences.get().setShowNfcDuringSwap(!isChecked);
}
});
return view;
}
}

View File

@ -1,4 +1,4 @@
package org.fdroid.fdroid.views.swap.views;
package org.fdroid.fdroid.views.swap;
import android.annotation.TargetApi;
import android.content.Context;

View File

@ -1,319 +0,0 @@
package org.fdroid.fdroid.views.swap;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.CursorAdapter;
import android.support.v7.widget.SearchView;
import android.text.TextUtils;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.data.InstalledAppProvider;
import org.fdroid.fdroid.localrepo.LocalRepoManager;
import org.fdroid.fdroid.views.fragments.ThemeableListFragment;
import java.util.HashSet;
import java.util.Set;
public class SelectAppsFragment extends ThemeableListFragment
implements LoaderManager.LoaderCallbacks<Cursor>, SearchView.OnQueryTextListener {
@SuppressWarnings("UnusedDeclaration")
private static final String TAG = "SwapAppsList";
private String mCurrentFilterString;
@NonNull
private final Set<String> previouslySelectedApps = new HashSet<>();
public Set<String> getSelectedApps() {
return FDroidApp.selectedApps;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
menuInflater.inflate(R.menu.swap_next_search, menu);
MenuItem nextMenuItem = menu.findItem(R.id.action_next);
int flags = MenuItemCompat.SHOW_AS_ACTION_ALWAYS | MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT;
MenuItemCompat.setShowAsAction(nextMenuItem, flags);
SearchView searchView = new SearchView(getActivity());
MenuItem searchMenuItem = menu.findItem(R.id.action_search);
MenuItemCompat.setActionView(searchMenuItem, searchView);
MenuItemCompat.setShowAsAction(searchMenuItem, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
searchView.setOnQueryTextListener(this);
}
@Override
public void onResume() {
super.onResume();
previouslySelectedApps.clear();
if (FDroidApp.selectedApps != null) {
previouslySelectedApps.addAll(FDroidApp.selectedApps);
}
}
public boolean hasSelectionChanged() {
Set<String> currentlySelected = getSelectedApps();
if (currentlySelected.size() != previouslySelectedApps.size()) {
return true;
}
for (String current : currentlySelected) {
boolean found = false;
for (String previous : previouslySelectedApps) {
if (current.equals(previous)) {
found = true;
break;
}
}
if (!found) {
return true;
}
}
return false;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setEmptyText(getString(R.string.no_applications_found));
ListView listView = getListView();
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
setListAdapter(new AppListAdapter(listView, getActivity(), null));
setListShown(false); // start out with a progress indicator
// either reconnect with an existing loader or start a new one
getLoaderManager().initLoader(0, null, this);
// build list of existing apps from what is on the file system
if (FDroidApp.selectedApps == null) {
FDroidApp.selectedApps = new HashSet<>();
for (String filename : LocalRepoManager.get(getActivity()).repoDir.list()) {
if (filename.matches(".*\\.apk")) {
String packageName = filename.substring(0, filename.indexOf("_"));
FDroidApp.selectedApps.add(packageName);
}
}
}
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Ignore the headerView at position 0.
if (position > 0) {
toggleAppSelected(position);
}
}
private void toggleAppSelected(int position) {
Cursor c = (Cursor) getListAdapter().getItem(position - 1);
String packageName = c.getString(c.getColumnIndex(InstalledAppProvider.DataColumns.APP_ID));
if (FDroidApp.selectedApps.contains(packageName)) {
FDroidApp.selectedApps.remove(packageName);
} else {
FDroidApp.selectedApps.add(packageName);
}
}
@Override
public CursorLoader onCreateLoader(int id, Bundle args) {
Uri baseUri;
if (TextUtils.isEmpty(mCurrentFilterString)) {
baseUri = InstalledAppProvider.getContentUri();
} else {
baseUri = InstalledAppProvider.getSearchUri(mCurrentFilterString);
}
return new CursorLoader(
this.getActivity(),
baseUri,
InstalledAppProvider.DataColumns.ALL,
null,
null,
InstalledAppProvider.DataColumns.APPLICATION_LABEL);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
((AppListAdapter)getListAdapter()).swapCursor(cursor);
ListView listView = getListView();
String fdroid = loader.getContext().getPackageName();
for (int i = 0; i < listView.getCount() - 1; i++) {
Cursor c = ((Cursor) listView.getItemAtPosition(i + 1));
String packageName = c.getString(c.getColumnIndex(InstalledAppProvider.DataColumns.APP_ID));
if (TextUtils.equals(packageName, fdroid)) {
listView.setItemChecked(i + 1, true); // always include FDroid
} else {
for (String selected : FDroidApp.selectedApps) {
if (TextUtils.equals(packageName, selected)) {
listView.setItemChecked(i + 1, true);
}
}
}
}
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
((AppListAdapter)getListAdapter()).swapCursor(null);
}
@Override
public boolean onQueryTextChange(String newText) {
String newFilter = !TextUtils.isEmpty(newText) ? newText : null;
if (mCurrentFilterString == null && newFilter == null) {
return true;
}
if (mCurrentFilterString != null && mCurrentFilterString.equals(newFilter)) {
return true;
}
mCurrentFilterString = newFilter;
getLoaderManager().restartLoader(0, null, this);
return true;
}
@Override
public boolean onQueryTextSubmit(String query) {
// this is not needed since we respond to every change in text
return true;
}
@Override
protected int getThemeStyle() {
return R.style.SwapTheme_StartSwap;
}
@Override
protected int getHeaderLayout() {
return R.layout.swap_create_header;
}
private class AppListAdapter extends CursorAdapter {
@SuppressWarnings("UnusedDeclaration")
private static final String TAG = "AppListAdapter";
@Nullable
private LayoutInflater inflater;
@Nullable
private Drawable defaultAppIcon;
@NonNull
private final ListView listView;
public AppListAdapter(@NonNull ListView listView, @NonNull Context context, @Nullable Cursor c) {
super(context, c, FLAG_REGISTER_CONTENT_OBSERVER);
this.listView = listView;
}
@NonNull
private LayoutInflater getInflater(Context context) {
if (inflater == null) {
Context themedContext = new ContextThemeWrapper(context, R.style.SwapTheme_AppList_ListItem);
inflater = (LayoutInflater)themedContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
return inflater;
}
@NonNull
private Drawable getDefaultAppIcon(Context context) {
if (defaultAppIcon == null) {
defaultAppIcon = context.getResources().getDrawable(android.R.drawable.sym_def_app_icon);
}
return defaultAppIcon;
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = getInflater(context).inflate(R.layout.select_local_apps_list_item, parent, false);
bindView(view, context, cursor);
return view;
}
@Override
public void bindView(final View view, final Context context, final Cursor cursor) {
TextView packageView = (TextView)view.findViewById(R.id.package_name);
TextView labelView = (TextView)view.findViewById(R.id.application_label);
ImageView iconView = (ImageView)view.findViewById(android.R.id.icon);
String packageName = cursor.getString(cursor.getColumnIndex(InstalledAppProvider.DataColumns.APP_ID));
String appLabel = cursor.getString(cursor.getColumnIndex(InstalledAppProvider.DataColumns.APPLICATION_LABEL));
Drawable icon;
try {
icon = context.getPackageManager().getApplicationIcon(packageName);
} catch (PackageManager.NameNotFoundException e) {
icon = getDefaultAppIcon(context);
}
packageView.setText(packageName);
labelView.setText(appLabel);
iconView.setImageDrawable(icon);
// Since v11, the Android SDK provided the ability to show selected list items
// by highlighting their background. Prior to this, we need to handle this ourselves
// by adding a checkbox which can toggle selected items.
View checkBoxView = view.findViewById(R.id.checkbox);
if (checkBoxView != null) {
CheckBox checkBox = (CheckBox)checkBoxView;
checkBox.setOnCheckedChangeListener(null);
final int listPosition = cursor.getPosition() + 1; // To account for the header view.
checkBox.setChecked(listView.isItemChecked(listPosition));
checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
listView.setItemChecked(listPosition, isChecked);
toggleAppSelected(listPosition);
}
});
}
}
}
}

View File

@ -1,4 +1,4 @@
package org.fdroid.fdroid.views.swap.views;
package org.fdroid.fdroid.views.swap;
import android.annotation.TargetApi;
import android.content.Context;
@ -35,7 +35,6 @@ import android.widget.TextView;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.data.InstalledAppProvider;
import org.fdroid.fdroid.localrepo.SwapState;
import org.fdroid.fdroid.views.swap.SwapWorkflowActivity;
public class SelectAppsView extends ListView implements
SwapWorkflowActivity.InnerView,

View File

@ -1,22 +0,0 @@
package org.fdroid.fdroid.views.swap;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.fdroid.fdroid.R;
public class StartSwapFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
LayoutInflater themedInflater = (LayoutInflater)new ContextThemeWrapper(inflater.getContext(), R.style.SwapTheme_StartSwap).getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return themedInflater.inflate(R.layout.swap_blank, container, false);
}
}

View File

@ -1,4 +1,4 @@
package org.fdroid.fdroid.views.swap.views;
package org.fdroid.fdroid.views.swap;
import android.annotation.TargetApi;
import android.content.Context;

View File

@ -1,295 +0,0 @@
package org.fdroid.fdroid.views.swap;
import android.app.ProgressDialog;
import android.content.Context;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.ActionBarActivity;
import android.view.MenuItem;
import android.widget.Toast;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.NfcHelper;
import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.localrepo.LocalRepoManager;
import org.fdroid.fdroid.localrepo.SwapState;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
public class SwapActivity extends ActionBarActivity implements SwapProcessManager {
private static final String STATE_START_SWAP = "startSwap";
private static final String STATE_SELECT_APPS = "selectApps";
private static final String STATE_JOIN_WIFI = "joinWifi";
private static final String STATE_NFC = "nfc";
private static final String STATE_WIFI_QR = "wifiQr";
private Timer shutdownLocalRepoTimer;
private UpdateAsyncTask updateSwappableAppsTask = null;
private boolean hasPreparedLocalRepo = false;
@Override
public void onBackPressed() {
switch (currentState()) {
case STATE_START_SWAP:
finish();
break;
default:
super.onBackPressed();
break;
}
}
private String currentState() {
FragmentManager.BackStackEntry lastFragment = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1);
return lastFragment.getName();
}
public void nextStep() {
switch (currentState()) {
case STATE_START_SWAP:
showSelectApps();
break;
case STATE_SELECT_APPS:
prepareLocalRepo();
break;
case STATE_JOIN_WIFI:
ensureLocalRepoRunning();
if (!attemptToShowNfc()) {
showWifiQr();
}
break;
case STATE_NFC:
showWifiQr();
break;
case STATE_WIFI_QR:
break;
}
supportInvalidateOptionsMenu();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_next) {
nextStep();
}
return super.onOptionsItemSelected(item);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
setContentView(R.layout.swap_activity);
// Necessary to run on an Android 2.3.[something] device.
new Handler().post(new Runnable() {
@Override
public void run() {
showFragment(new StartSwapFragment(), STATE_START_SWAP);
if (getState().isLocalRepoServiceRunning()) {
showSelectApps();
showJoinWifi();
attemptToShowNfc();
showWifiQr();
}
}
});
}
}
public void showSelectApps() {
showFragment(new SelectAppsFragment(), STATE_SELECT_APPS);
}
private void showJoinWifi() {
showFragment(new JoinWifiFragment(), STATE_JOIN_WIFI);
}
private boolean attemptToShowNfc() {
// TODO: What if NFC is disabled? Hook up with NfcNotEnabledActivity? Or maybe only if they
// click a relevant button?
// Even if they opted to skip the message which says "Touch devices to swap",
// we still want to actually enable the feature, so that they could touch
// during the wifi qr code being shown too.
boolean nfcMessageReady = NfcHelper.setPushMessage(this, Utils.getSharingUri(FDroidApp.repo));
if (Preferences.get().showNfcDuringSwap() && nfcMessageReady) {
showFragment(new NfcSwapFragment(), STATE_NFC);
return true;
}
return false;
}
private void showBluetooth() {
}
private void showWifiQr() {
showFragment(new WifiQrFragment(), STATE_WIFI_QR);
}
private void showFragment(Fragment fragment, String name) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, fragment, name)
.addToBackStack(name)
.commit();
}
private void prepareLocalRepo() {
SelectAppsFragment fragment = (SelectAppsFragment)getSupportFragmentManager().findFragmentByTag(STATE_SELECT_APPS);
boolean needsUpdating = !hasPreparedLocalRepo || fragment.hasSelectionChanged();
if (updateSwappableAppsTask == null && needsUpdating) {
updateSwappableAppsTask = new UpdateAsyncTask(this, fragment.getSelectedApps());
updateSwappableAppsTask.execute();
} else {
showJoinWifi();
}
}
/**
* Once the UpdateAsyncTask has finished preparing our repository index, we can
* show the next screen to the user.
*/
private void onLocalRepoPrepared() {
updateSwappableAppsTask = null;
hasPreparedLocalRepo = true;
showJoinWifi();
}
private void ensureLocalRepoRunning() {
if (!getState().isLocalRepoServiceRunning()) {
getState().startLocalRepoService();
initLocalRepoTimer(900000); // 15 mins
}
}
private SwapState getState() {
return SwapState.load(this);
}
private void initLocalRepoTimer(long timeoutMilliseconds) {
// reset the timer if viewing this Activity again
if (shutdownLocalRepoTimer != null)
shutdownLocalRepoTimer.cancel();
// automatically turn off after 15 minutes
shutdownLocalRepoTimer = new Timer();
shutdownLocalRepoTimer.schedule(new TimerTask() {
@Override
public void run() {
getState().stopLocalRepoService();
}
}, timeoutMilliseconds);
}
@Override
public void stopSwapping() {
if (getState().isLocalRepoServiceRunning()) {
if (shutdownLocalRepoTimer != null) {
shutdownLocalRepoTimer.cancel();
}
getState().stopLocalRepoService();
}
finish();
}
class UpdateAsyncTask extends AsyncTask<Void, String, Void> {
@SuppressWarnings("UnusedDeclaration")
private static final String TAG = "SwapActivity.UpdateAsyncTask";
@NonNull
private final ProgressDialog progressDialog;
@NonNull
private final Set<String> selectedApps;
@NonNull
private final Uri sharingUri;
public UpdateAsyncTask(Context c, @NonNull Set<String> apps) {
selectedApps = apps;
progressDialog = new ProgressDialog(c);
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progressDialog.setTitle(R.string.updating);
sharingUri = Utils.getSharingUri(FDroidApp.repo);
}
@Override
protected void onPreExecute() {
progressDialog.show();
}
@Override
protected Void doInBackground(Void... params) {
try {
final LocalRepoManager lrm = LocalRepoManager.get(SwapActivity.this);
publishProgress(getString(R.string.deleting_repo));
lrm.deleteRepo();
for (String app : selectedApps) {
publishProgress(String.format(getString(R.string.adding_apks_format), app));
lrm.addApp(SwapActivity.this, app);
}
lrm.writeIndexPage(sharingUri.toString());
publishProgress(getString(R.string.writing_index_jar));
lrm.writeIndexJar();
publishProgress(getString(R.string.linking_apks));
lrm.copyApksToRepo();
publishProgress(getString(R.string.copying_icons));
// run the icon copy without progress, its not a blocker
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
lrm.copyIconsToRepo();
return null;
}
}.execute();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onProgressUpdate(String... progress) {
super.onProgressUpdate(progress);
progressDialog.setMessage(progress[0]);
}
@Override
protected void onPostExecute(Void result) {
progressDialog.dismiss();
Toast.makeText(SwapActivity.this, R.string.updated_local_repo, Toast.LENGTH_SHORT).show();
onLocalRepoPrepared();
}
}
}

View File

@ -1,12 +0,0 @@
package org.fdroid.fdroid.views.swap;
/**
* Defines the contract between the {@link org.fdroid.fdroid.views.swap.SwapActivity}
* and the fragments which live in it. The fragments each have the responsibility of
* moving to the next stage of the process, and are entitled to stop swapping too
* (e.g. when a "Cancel" button is pressed).
*/
public interface SwapProcessManager {
void nextStep();
void stopSwapping();
}

View File

@ -1,164 +0,0 @@
package org.fdroid.fdroid.views.swap;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.LightingColorFilter;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.fdroid.fdroid.FDroid;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.QrGenAsyncTask;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.NewRepoConfig;
import org.fdroid.fdroid.net.WifiStateChangeService;
import java.net.URI;
import java.util.List;
import java.util.Locale;
public class WifiQrFragment extends Fragment {
private static final int CONNECT_TO_SWAP = 1;
private static final String TAG = "WifiQrFragment";
private final BroadcastReceiver onWifiChange = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent i) {
setUIFromWifi();
}
};
private SwapProcessManager swapManager;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.swap_wifi_qr, container, false);
ImageView qrImage = (ImageView)view.findViewById(R.id.wifi_qr_code);
// Replace all blacks with the background blue.
qrImage.setColorFilter(new LightingColorFilter(0xffffffff, getResources().getColor(R.color.swap_blue)));
Button openQr = (Button)view.findViewById(R.id.btn_qr_scanner);
openQr.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
IntentIntegrator integrator = new IntentIntegrator(WifiQrFragment.this);
integrator.initiateScan();
}
});
Button cancel = (Button)view.findViewById(R.id.btn_cancel_swap);
cancel.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
swapManager.stopSwapping();
}
});
return view;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
swapManager = (SwapProcessManager)activity;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
if (scanResult != null) {
if (scanResult.getContents() != null) {
NewRepoConfig repoConfig = new NewRepoConfig(getActivity(), scanResult.getContents());
if (repoConfig.isValidRepo()) {
startActivityForResult(new Intent(FDroid.ACTION_ADD_REPO, Uri.parse(scanResult.getContents()), getActivity(), ConnectSwapActivity.class), CONNECT_TO_SWAP);
} else {
Toast.makeText(getActivity(), "The QR code you scanned doesn't look like a swap code.", Toast.LENGTH_SHORT).show();
}
}
} else if (requestCode == CONNECT_TO_SWAP && resultCode == Activity.RESULT_OK) {
getActivity().finish();
}
}
public void onResume() {
super.onResume();
setUIFromWifi();
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(onWifiChange,
new IntentFilter(WifiStateChangeService.BROADCAST));
}
private void setUIFromWifi() {
if (TextUtils.isEmpty(FDroidApp.repo.address) || getView() == null)
return;
String scheme = Preferences.get().isLocalRepoHttpsEnabled() ? "https://" : "http://";
// the fingerprint is not useful on the button label
String buttonLabel = scheme + FDroidApp.ipAddressString + ":" + FDroidApp.port;
TextView ipAddressView = (TextView) getView().findViewById(R.id.device_ip_address);
ipAddressView.setText(buttonLabel);
/*
* Set URL to UPPER for compact QR Code, FDroid will translate it back.
* Remove the SSID from the query string since SSIDs are case-sensitive.
* Instead the receiver will have to rely on the BSSID to find the right
* wifi AP to join. Lots of QR Scanners are buggy and do not respect
* custom URI schemes, so we have to use http:// or https:// :-(
*/
Uri sharingUri = Utils.getSharingUri(FDroidApp.repo);
String qrUriString = (scheme + sharingUri.getHost()).toUpperCase(Locale.ENGLISH);
if (sharingUri.getPort() != 80) {
qrUriString += ":" + sharingUri.getPort();
}
qrUriString += sharingUri.getPath().toUpperCase(Locale.ENGLISH);
boolean first = true;
// Andorid provides an API for getting the query parameters and iterating over them:
// Uri.getQueryParameterNames()
// But it is only available on later Android versions. As such we use URLEncodedUtils instead.
List<NameValuePair> parameters = URLEncodedUtils.parse(URI.create(sharingUri.toString()), "UTF-8");
for (NameValuePair parameter : parameters) {
if (!parameter.getName().equals("ssid")) {
if (first) {
qrUriString += "?";
first = false;
} else {
qrUriString += "&";
}
qrUriString += parameter.getName().toUpperCase(Locale.ENGLISH) + "=" +
parameter.getValue().toUpperCase(Locale.ENGLISH);
}
}
Log.i(TAG, "Encoded swap URI in QR Code: " + qrUriString);
new QrGenAsyncTask(getActivity(), R.id.wifi_qr_code).execute(qrUriString);
}
}

View File

@ -1,4 +1,4 @@
package org.fdroid.fdroid.views.swap.views;
package org.fdroid.fdroid.views.swap;
import android.annotation.TargetApi;
import android.content.BroadcastReceiver;