diff --git a/app/build.properties b/app/build.properties index a2b05823b..df9cfe4c1 100644 --- a/app/build.properties +++ b/app/build.properties @@ -1,7 +1,7 @@ #Build Properties -#Sat Nov 24 09:14:14 EST 2018 +#Wed Jan 02 20:18:46 CST 2019 version_minor=0 -version_build=0 +version_build=4 version_patch=3 version_store=44 version_major=2 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 07ba62b9c..0794c2479 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,6 +23,8 @@ + + = Build.VERSION_CODES.M) { if (activity.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED || - activity.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + activity.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED|| + activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED || + activity.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { return; } if (activity.isFinishing()) { return; } activity.requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, REQUEST_CODE); + activity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE); } } diff --git a/app/src/main/java/com/vrem/wifianalyzer/navigation/NavigationGroup.java b/app/src/main/java/com/vrem/wifianalyzer/navigation/NavigationGroup.java index 940274cf0..7ba45ac49 100644 --- a/app/src/main/java/com/vrem/wifianalyzer/navigation/NavigationGroup.java +++ b/app/src/main/java/com/vrem/wifianalyzer/navigation/NavigationGroup.java @@ -26,8 +26,8 @@ import java.util.List; public enum NavigationGroup { - GROUP_FEATURE(NavigationMenu.ACCESS_POINTS, NavigationMenu.CHANNEL_RATING, NavigationMenu.CHANNEL_GRAPH, NavigationMenu.TIME_GRAPH), - GROUP_OTHER(NavigationMenu.EXPORT, NavigationMenu.CHANNEL_AVAILABLE, NavigationMenu.VENDORS), + GROUP_FEATURE(NavigationMenu.ACCESS_POINTS), + GROUP_OTHER(NavigationMenu.SET_DESTINATION, NavigationMenu.COLLECT_DATA), GROUP_SETTINGS(NavigationMenu.SETTINGS, NavigationMenu.ABOUT); private final List navigationMenus; diff --git a/app/src/main/java/com/vrem/wifianalyzer/navigation/NavigationMenu.java b/app/src/main/java/com/vrem/wifianalyzer/navigation/NavigationMenu.java index 82b7ca268..3f4bc23e6 100644 --- a/app/src/main/java/com/vrem/wifianalyzer/navigation/NavigationMenu.java +++ b/app/src/main/java/com/vrem/wifianalyzer/navigation/NavigationMenu.java @@ -35,12 +35,8 @@ public enum NavigationMenu { ACCESS_POINTS(R.drawable.ic_network_wifi_grey_500_48dp, R.string.action_access_points, NavigationItemFactory.ACCESS_POINTS, NavigationOptionFactory.AP), - CHANNEL_RATING(R.drawable.ic_wifi_tethering_grey_500_48dp, R.string.action_channel_rating, NavigationItemFactory.CHANNEL_RATING, NavigationOptionFactory.RATING), - CHANNEL_GRAPH(R.drawable.ic_insert_chart_grey_500_48dp, R.string.action_channel_graph, NavigationItemFactory.CHANNEL_GRAPH, NavigationOptionFactory.OTHER), - TIME_GRAPH(R.drawable.ic_show_chart_grey_500_48dp, R.string.action_time_graph, NavigationItemFactory.TIME_GRAPH, NavigationOptionFactory.OTHER), - EXPORT(R.drawable.ic_import_export_grey_500_48dp, R.string.action_export, NavigationItemFactory.EXPORT), - CHANNEL_AVAILABLE(R.drawable.ic_location_on_grey_500_48dp, R.string.action_channel_available, NavigationItemFactory.CHANNEL_AVAILABLE), - VENDORS(R.drawable.ic_list_grey_500_48dp, R.string.action_vendors, NavigationItemFactory.VENDORS), + SET_DESTINATION(R.drawable.ic_wifi_tethering_grey_500_48dp, R.string.set_destination, NavigationItemFactory.SET_DESTINATION, NavigationOptionFactory.OTHER), + COLLECT_DATA(R.drawable.ic_show_chart_grey_500_48dp, R.string.collect_data, NavigationItemFactory.COLLECT_DATA, NavigationOptionFactory.OTHER), SETTINGS(R.drawable.ic_settings_grey_500_48dp, R.string.action_settings, NavigationItemFactory.SETTINGS), ABOUT(R.drawable.ic_info_outline_grey_500_48dp, R.string.action_about, NavigationItemFactory.ABOUT); diff --git a/app/src/main/java/com/vrem/wifianalyzer/navigation/items/NavigationItemFactory.java b/app/src/main/java/com/vrem/wifianalyzer/navigation/items/NavigationItemFactory.java index 49bdc1310..828a9e8b1 100644 --- a/app/src/main/java/com/vrem/wifianalyzer/navigation/items/NavigationItemFactory.java +++ b/app/src/main/java/com/vrem/wifianalyzer/navigation/items/NavigationItemFactory.java @@ -22,21 +22,14 @@ import com.vrem.wifianalyzer.about.AboutFragment; import com.vrem.wifianalyzer.settings.SettingsFragment; -import com.vrem.wifianalyzer.vendor.VendorFragment; import com.vrem.wifianalyzer.wifi.accesspoint.AccessPointsFragment; -import com.vrem.wifianalyzer.wifi.channelavailable.ChannelAvailableFragment; -import com.vrem.wifianalyzer.wifi.channelgraph.ChannelGraphFragment; -import com.vrem.wifianalyzer.wifi.channelrating.ChannelRatingFragment; -import com.vrem.wifianalyzer.wifi.timegraph.TimeGraphFragment; +import com.vrem.wifianalyzer.wifi.collectdata.CollectDataFragment; +import com.vrem.wifianalyzer.wifi.setdestination.SetTheDestinationFragment; public class NavigationItemFactory { public static final NavigationItem ACCESS_POINTS = new FragmentItem(new AccessPointsFragment()); - public static final NavigationItem CHANNEL_RATING = new FragmentItem(new ChannelRatingFragment()); - public static final NavigationItem CHANNEL_GRAPH = new FragmentItem(new ChannelGraphFragment()); - public static final NavigationItem TIME_GRAPH = new FragmentItem(new TimeGraphFragment()); - public static final NavigationItem EXPORT = new ExportItem(); - public static final NavigationItem CHANNEL_AVAILABLE = new FragmentItem(new ChannelAvailableFragment(), false); - public static final NavigationItem VENDORS = new FragmentItem(new VendorFragment(), false, View.GONE); + public static final NavigationItem SET_DESTINATION = new FragmentItem(new SetTheDestinationFragment()); + public static final NavigationItem COLLECT_DATA = new FragmentItem(new CollectDataFragment()); public static final NavigationItem SETTINGS = new FragmentItem(new SettingsFragment(), false, View.GONE); public static final NavigationItem ABOUT = new FragmentItem(new AboutFragment(), false, View.GONE); diff --git a/app/src/main/java/com/vrem/wifianalyzer/wifi/collectdata/CollectDataFragment.java b/app/src/main/java/com/vrem/wifianalyzer/wifi/collectdata/CollectDataFragment.java new file mode 100644 index 000000000..e823bf627 --- /dev/null +++ b/app/src/main/java/com/vrem/wifianalyzer/wifi/collectdata/CollectDataFragment.java @@ -0,0 +1,142 @@ +package com.vrem.wifianalyzer.wifi.collectdata; + +import android.content.Context; +import android.os.Bundle; +import android.os.Environment; +import android.support.annotation.NonNull; +import android.support.v4.app.Fragment; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Toast; + +import com.vrem.wifianalyzer.MainContext; +import com.vrem.wifianalyzer.R; +import com.vrem.wifianalyzer.settings.Settings; +import com.vrem.wifianalyzer.wifi.band.WiFiBand; +import com.vrem.wifianalyzer.wifi.model.SortBy; +import com.vrem.wifianalyzer.wifi.model.WiFiData; +import com.vrem.wifianalyzer.wifi.model.WiFiDetail; +import com.vrem.wifianalyzer.wifi.predicate.WiFiBandPredicate; +import com.vrem.wifianalyzer.wifi.scanner.UpdateNotifier; + +import org.apache.commons.collections4.Predicate; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.List; + +import static android.content.ContentValues.TAG; + + +public class CollectDataFragment extends Fragment implements UpdateNotifier{ + + private Button saveBtn; + private EditText currentLocationEt; + private static String objectString = ""; + private static JSONObject jsonObject=new JSONObject(); + + + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + View view = inflater.inflate(R.layout.collect_data, container, false); + MainContext.INSTANCE.getScannerService().register(this::update); + + saveBtn = view.findViewById(R.id.saveCollectedData); + currentLocationEt = view.findViewById(R.id.collectDataEt); + + // loop through wifiDetails and put() all bssid and current location + // call writeJSONtoFile with !!jsonObject!! IFF we really added any new values + + saveBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + + if (currentLocationEt.getText().toString().isEmpty()) + try { + Log.d(TAG, "onClick: " + WiFiData.wiFiDetails.get(0).getBSSID()+" "+WiFiData.wiFiDetails.get(0).getTitle().split(" ")[0]); + jsonObject.put(WiFiData.wiFiDetails.get(0).getBSSID(),WiFiData.wiFiDetails.get(0).getTitle().split(" ")[0]); + writeJSONtoFile(MainContext.INSTANCE.getContext(), currentLocationEt.getText().toString(), jsonObject); + } catch (JSONException e) { + e.printStackTrace(); + } + } + }); + + /* readJSON(currentLocationEt.toString());*/ + return view; + } + + + @Override + public void update(@NonNull WiFiData wiFiData) { + + Settings settings = MainContext.INSTANCE.getSettings(); + WiFiBand wiFiBand = settings.getWiFiBand(); + Predicate predicate = new WiFiBandPredicate(wiFiBand); + WiFiData.wiFiDetails = wiFiData.getWiFiDetails(predicate, SortBy.STRENGTH); + } + + private String readJSON(String filename) { + File directory = Environment.getExternalStorageDirectory(); + File file = new File(directory + "/Notes/", filename); + + StringBuilder text = new StringBuilder(); + try { + BufferedReader br = new BufferedReader(new FileReader(file)); + String line; + + while ((line = br.readLine()) != null) { + + text.append(line); + text.append('\n'); + } + JSONObject jsonObject = new JSONObject(String.valueOf(text)); + for (int i = 0; i - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - */ - package com.vrem.wifianalyzer.wifi.model; +import android.content.Context; +import android.os.Environment; import android.support.annotation.NonNull; +import android.util.Log; +import android.widget.Toast; +import com.vrem.util.FileUtils; import com.vrem.wifianalyzer.MainContext; +import com.vrem.wifianalyzer.R; import com.vrem.wifianalyzer.vendor.model.VendorService; import org.apache.commons.collections4.CollectionUtils; @@ -28,18 +16,47 @@ import org.apache.commons.collections4.Predicate; import org.apache.commons.collections4.Transformer; import org.apache.commons.lang3.builder.EqualsBuilder; - +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; + +import static android.content.ContentValues.TAG; + public class WiFiData { - public static final WiFiData EMPTY = new WiFiData(Collections.emptyList(), WiFiConnection.EMPTY, Collections.emptyList()); - private final List wiFiDetails; - private final WiFiConnection wiFiConnection; + public static final WiFiData EMPTY = new WiFiData(Collections.emptyList(), WiFiConnection.EMPTY, Collections.emptyList()); + public static String location = ""; + public static List wiFiDetails; private final List wiFiConfigurations; + private Context mContext = MainContext.INSTANCE.getContext(); + private final WiFiConnection wiFiConnection; + private static JSONObject wiFiLocations = new JSONObject(); + + + static + + { + try { + + String content = FileUtils.readFile(MainContext.INSTANCE.getResources(), R.raw.shanghai); + wiFiLocations = new JSONObject(content); + Toast.makeText(MainContext.INSTANCE.getContext(), "Loaded locations with " + wiFiLocations.length() + " MAC -> location mappings", Toast.LENGTH_LONG).show(); + } catch (Exception e) { + Toast.makeText(MainContext.INSTANCE.getContext(), "Failed to load locations", Toast.LENGTH_LONG).show(); + } + } + + private static String lastNotified = ""; public WiFiData(@NonNull List wiFiDetails, @NonNull WiFiConnection wiFiConnection, @NonNull List wiFiConfigurations) { this.wiFiDetails = wiFiDetails; @@ -65,6 +82,7 @@ public List getWiFiDetails(@NonNull Predicate predicate, results = sortAndGroup(results, sortBy, groupBy); } Collections.sort(results, sortBy.comparator()); + notifyLocation(results); return results; } @@ -91,6 +109,47 @@ List sortAndGroup(@NonNull List wiFiDetails, @NonNull So return results; } + + private void notifyLocation(List wifiDetails) { + + + if (wifiDetails.size() < 3) { + return; + } + try { + String lookupKey = (wifiDetails.get(0).getBSSID().toUpperCase()/*+" " + wifiDetails.get(0).getWiFiSignal().getStrength()+"\n"+wifiDetails.get(1).getBSSID().toUpperCase()+" " + wifiDetails.get(1).getWiFiSignal().getStrength()+"\n"+wifiDetails.get(2).getBSSID().toUpperCase()+" " + wifiDetails.get(2).getWiFiSignal().getStrength()*/); + Log.d(TAG, "Wifidata: " + lookupKey); + location = wiFiLocations.getString(lookupKey); + if (location == null) { + location = wiFiLocations.getString((wifiDetails.get(1).getBSSID() + " " + wifiDetails.get(0).getBSSID() + " " + wifiDetails.get(2).getBSSID()).toUpperCase()); + } + if (location == null) { + location = wiFiLocations.getString((wifiDetails.get(0).getBSSID() + " " + wifiDetails.get(1).getBSSID()).toUpperCase()); + } + if (location == null) { + location = wiFiLocations.getString((wifiDetails.get(1).getBSSID() + " " + wifiDetails.get(0).getBSSID()).toUpperCase()); + } + if (location == null) { + location = wiFiLocations.getString(wifiDetails.get(0).getBSSID().toUpperCase()); + } + if (location == null) { + location = wiFiLocations.getString(wifiDetails.get(1).getBSSID().toUpperCase()); + } + if (location != null) { + if (!lastNotified.equals(location)) { + Log.d(TAG, "notifyLocation: "+location); + Log.d(TAG, "notifyLocation: "+lastNotified); + Toast.makeText(MainContext.INSTANCE.getContext(), "You are in " + location, Toast.LENGTH_LONG).show(); + lastNotified = location; + + } + } else { + //Toast.makeText(MainContext.INSTANCE.getContext(), "Nothing found " + lookupKey, Toast.LENGTH_SHORT).show(); + } + } catch (Exception e) { + } + } + @NonNull private List getWiFiDetails(@NonNull Predicate predicate) { Collection selected = CollectionUtils.select(wiFiDetails, predicate); @@ -125,9 +184,9 @@ private class ConnectionPredicate implements Predicate { @Override public boolean evaluate(WiFiDetail wiFiDetail) { return new EqualsBuilder() - .append(wiFiConnection.getSSID(), wiFiDetail.getSSID()) - .append(wiFiConnection.getBSSID(), wiFiDetail.getBSSID()) - .isEquals(); + .append(wiFiConnection.getSSID(), wiFiDetail.getSSID()) + .append(wiFiConnection.getBSSID(), wiFiDetail.getBSSID()) + .isEquals(); } } @@ -152,4 +211,4 @@ public WiFiDetail transform(WiFiDetail input) { } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/vrem/wifianalyzer/wifi/setdestination/SetTheDestinationFragment.java b/app/src/main/java/com/vrem/wifianalyzer/wifi/setdestination/SetTheDestinationFragment.java new file mode 100644 index 000000000..bb5e78041 --- /dev/null +++ b/app/src/main/java/com/vrem/wifianalyzer/wifi/setdestination/SetTheDestinationFragment.java @@ -0,0 +1,200 @@ + +package com.vrem.wifianalyzer.wifi.setdestination; + + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.graphics.BitmapFactory; +import android.graphics.Color; +import android.media.RingtoneManager; +import android.os.Build; +import android.os.Bundle; +import android.os.VibrationEffect; +import android.os.Vibrator; +import android.support.annotation.NonNull; +import android.support.v4.app.Fragment; +import android.support.v4.widget.SwipeRefreshLayout; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; + +import com.vrem.wifianalyzer.MainContext; +import com.vrem.wifianalyzer.R; +import com.vrem.wifianalyzer.settings.Settings; +import com.vrem.wifianalyzer.wifi.band.WiFiBand; +import com.vrem.wifianalyzer.wifi.model.SortBy; +import com.vrem.wifianalyzer.wifi.model.WiFiData; +import com.vrem.wifianalyzer.wifi.model.WiFiDetail; +import com.vrem.wifianalyzer.wifi.predicate.WiFiBandPredicate; +import com.vrem.wifianalyzer.wifi.scanner.UpdateNotifier; + +import org.apache.commons.collections4.Predicate; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static android.content.ContentValues.TAG; + + +public class SetTheDestinationFragment extends Fragment implements UpdateNotifier { + + + private List wiFiDetails; + private static TextView currentLocationTv; + private static String selectedDestination; + private static String lastNotified; + private SwipeRefreshLayout swipeRefreshLayout; + + @Override + public void update(@NonNull WiFiData wiFiData) { + setCurrentStationView(); + destinationHasBeenReached(); + Settings settings = MainContext.INSTANCE.getSettings(); + WiFiBand wiFiBand = settings.getWiFiBand(); + Predicate predicate = new WiFiBandPredicate(wiFiBand); + wiFiDetails = wiFiData.getWiFiDetails(predicate, SortBy.STRENGTH); + } + + private boolean destinationHasBeenReached() { + boolean arrived = false; + if (WiFiData.location.equals(selectedDestination)&&!WiFiData.location.equals(lastNotified)) { + headsUpNotification(); + vibrate(); + lastNotified = WiFiData.location; + arrived = true; + } + return arrived; +} + + private void setCurrentStationView() { + if (currentLocationTv == null) { + currentLocationTv.setText("Unknown"); + } else { + currentLocationTv.setText(WiFiData.location); + } + } + + + private void vibrate() { + Vibrator v = (Vibrator) MainContext.INSTANCE.getContext().getSystemService(Context.VIBRATOR_SERVICE); + long[] pattern = {0, 1000, 200}; + v.vibrate(pattern,2); + Log.d(TAG, "vibrate: it is called"); + + } + + private void headsUpNotification(){ + + Intent intent = new Intent(MainContext.INSTANCE.getContext(), SetTheDestinationFragment.class); + PendingIntent pi = PendingIntent.getActivity(MainContext.INSTANCE.getContext(), 0, intent, 0); + Notification.Builder builder = new Notification.Builder(MainContext.INSTANCE.getContext()); + + builder.setContentTitle(WiFiData.location.toString()) + .setContentText("You have arrived your destinantion") + .setSmallIcon(R.mipmap.ic_launcher) + .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)) + .setContentIntent(pi) + .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)) + .setPriority(Notification.PRIORITY_MAX); + + NotificationManager notificationManager = (NotificationManager) MainContext.INSTANCE.getContext().getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.notify(0, builder.build()); + + } + + + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + View view = inflater.inflate(R.layout.set_destination, container, false); + MainContext.INSTANCE.getScannerService().register(this::update); + + currentLocationTv = view.findViewById(R.id.collectDatatext); + selectedDestination = ""; + + Spinner spinner = view.findViewById(R.id.random_spinner); + + + + // Initializing a String Array + String[] locations = new String[]{ + "终点 - Destination", + "MERCURY_29E6", + "***", + "B8 conference room", + "B#8 Women\'s restroom", + "epwj0016", + "B8 Classroom Door A" + }; + + + final List locationList = new ArrayList<>(Arrays.asList(locations)); + + // Initializing an ArrayAdapter + final ArrayAdapter spinnerArrayAdapter = new ArrayAdapter(MainContext.INSTANCE.getContext(),R.layout.spinner_item, locationList){ + @Override + public boolean isEnabled(int position){ + if(position == 0) + { + // Disable the first item from Spinner + // First item will be use for hint + return false; + } + else + { + return true; + } + } + @Override + public View getDropDownView(int position, View convertView, + ViewGroup parent) { + View view = super.getDropDownView(position, convertView, parent); + TextView tv = (TextView) view; + if(position == 0){ + // Set the hint text color gray + tv.setTextColor(Color.GRAY); + } + else { + tv.setTextColor(Color.BLACK); + } + return view; + } + }; + spinnerArrayAdapter.setDropDownViewResource(R.layout.spinner_item); + spinner.setAdapter(spinnerArrayAdapter); + + spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + selectedDestination = (String) parent.getItemAtPosition(position); + + // If user change the default selection + // First item is disable and it is used for hint + if(position > 0){ + // Notify the selected item text + Toast.makeText + (MainContext.INSTANCE.getContext(), "Selected : " + selectedDestination, Toast.LENGTH_SHORT) + .show(); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } + }); + + return view; + } + + +} \ No newline at end of file diff --git a/app/src/main/res/drawable/rounded_corner.xml b/app/src/main/res/drawable/rounded_corner.xml new file mode 100644 index 000000000..763253c08 --- /dev/null +++ b/app/src/main/res/drawable/rounded_corner.xml @@ -0,0 +1,16 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/collect_data.xml b/app/src/main/res/layout/collect_data.xml new file mode 100644 index 000000000..aadd22c97 --- /dev/null +++ b/app/src/main/res/layout/collect_data.xml @@ -0,0 +1,25 @@ + + + + +