14 Commits

12 changed files with 727 additions and 686 deletions
-1
View File
@@ -8,7 +8,6 @@
<option name="disableWrapperSourceDistributionNotification" value="true" /> <option name="disableWrapperSourceDistributionNotification" value="true" />
<option name="distributionType" value="DEFAULT_WRAPPED" /> <option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="11" />
<option name="modules"> <option name="modules">
<set> <set>
<option value="$PROJECT_DIR$" /> <option value="$PROJECT_DIR$" />
+13
View File
@@ -0,0 +1,13 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="ConstantConditions" enabled="true" level="WARNING" enabled_by_default="true">
<option name="SUGGEST_NULLABLE_ANNOTATIONS" value="false" />
<option name="DONT_REPORT_TRUE_ASSERT_STATEMENTS" value="false" />
</inspection_tool>
<inspection_tool class="SerializableHasSerialVersionUIDField" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreAnonymousInnerClasses" value="false" />
<option name="superClassString" value="java.awt.Component" />
</inspection_tool>
</profile>
</component>
+3 -3
View File
@@ -10,8 +10,8 @@ android {
applicationId "de.joel.zoomhelper" applicationId "de.joel.zoomhelper"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 30 targetSdkVersion 30
versionCode 13 versionCode 15
versionName '0.4.9' versionName '0.5.1'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
@@ -39,7 +39,7 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0' implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.lifecycle:lifecycle-process:2.2.0' implementation 'androidx.lifecycle:lifecycle-process:2.3.1'
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
@@ -0,0 +1,82 @@
package de.joel.zoomhelper;
import android.app.Activity;
import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;
import androidx.core.content.FileProvider;
import java.io.File;
import java.util.Timer;
import java.util.TimerTask;
public class APKDownloader {
public void downloadAPK(Context context, String fileName, Uri uri) {
Activity act = (Activity)context;
File destination = new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(), fileName);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
if (!context.getPackageManager().canRequestPackageInstalls()) {
Toast.makeText(context, R.string.PleaseAllowUnknownSources, Toast.LENGTH_LONG).show();
act.startActivityForResult(new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:de.joel.zoomhelper")), 101);
Timer timer = new Timer(true);
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (context.getPackageManager().canRequestPackageInstalls()) {
act.finishActivity(101);
downloadAPK(context, fileName, uri);
timer.cancel();
}
}
}, 0, 1000);
return;
}
}
//Delete update file if exists
final Uri localUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", destination);
if (destination.exists()) {
//noinspection ResultOfMethodCallIgnored
destination.delete();
}
//Download Script
DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(uri);
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
Log.d("Desturi", localUri.getPath());
request.setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOWNLOADS, fileName);
final long downloadID = downloadManager.enqueue(request);
BroadcastReceiver onDownloadComplete = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//Fetching the download id received with the broadcast
long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
//Checking if the received broadcast is for our enqueued download by matching download id
if (downloadID == id) {
Intent install = new Intent(Intent.ACTION_VIEW);
install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
install.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
install.setDataAndType(localUri,
downloadManager.getMimeTypeForDownloadedFile(downloadID));
context.startActivity(install);
context.unregisterReceiver(this);
}
}
};
context.registerReceiver(onDownloadComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
}
@@ -0,0 +1,30 @@
package de.joel.zoomhelper;
import java.io.Serializable;
public class Attendee implements Serializable {
private static final long serialVersionUID = 6196688086302483907L;
private String name;
private String num;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNum() {
return num;
}
public void setNum(String num) {
this.num = num;
}
public Attendee(String name, String num) {
this.name = name;
this.num = num;
}
}
@@ -0,0 +1,25 @@
package de.joel.zoomhelper;
import android.content.Context;
import android.util.AttributeSet;
public class InstantAutoComplete extends androidx.appcompat.widget.AppCompatAutoCompleteTextView {
public InstantAutoComplete(Context context) {
super(context);
}
public InstantAutoComplete(Context arg0, AttributeSet arg1) {
super(arg0, arg1);
}
public InstantAutoComplete(Context arg0, AttributeSet arg1, int arg2) {
super(arg0, arg1, arg2);
}
@Override
public boolean enoughToFilter() {
return false;
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,109 @@
package de.joel.zoomhelper;
import android.content.Intent;
import java.io.Serializable;
import java.util.ArrayList;
public class Meeting implements Serializable {
private static final long serialVersionUID = 2606722401897866931L;
private String meetingName;
private String meetingID;
private String meetingPWD;
private ArrayList<Attendee> attendees;
private int lastAtt;
public String getMeetingName() {
return meetingName;
}
public void setMeetingName(String meetingName) {
this.meetingName = meetingName;
}
public String getMeetingID() {
return meetingID;
}
public void setMeetingID(String meetingID) {
this.meetingID = meetingID;
}
public String getMeetingPWD() {
return meetingPWD;
}
public void setMeetingPWD(String meetingPWD) {
this.meetingPWD = meetingPWD;
}
public ArrayList<Attendee> getAttendees() {
return attendees;
}
public void setAttendees(ArrayList<Attendee> attendees) {
this.attendees = attendees;
}
public int getLastAtt() {
return lastAtt;
}
public void setLastAtt(int lastAtt) {
this.lastAtt = lastAtt;
}
public Meeting(String meetingName, String meetingID, String meetingPWD, String nameAttendee, String numAttendee) {
this.meetingName = meetingName;
this.meetingID = meetingID;
this.meetingPWD = meetingPWD;
this.attendees = new ArrayList<>();
Attendee attendee = new Attendee(nameAttendee, numAttendee);
this.attendees.add(attendee);
this.lastAtt = 0;
}
public void addAttendee(String name, String num) {
Attendee attendee = new Attendee(name, num);
this.attendees.add(attendee);
this.lastAtt = attendees.size() - 1;
}
public int searchAttendee(String name) {
int found = -1;
for (int i = 0; i < this.attendees.size(); i++) {
if (this.attendees.get(i).getName().equals(name)) {
found = i;
break;
}
}
return found;
}
public void updateAttendee(String name, String num) {
int found = searchAttendee(name);
if (found == -1) {
addAttendee(name, num);
} else {
this.attendees.get(found).setNum(num);
this.lastAtt = found;
}
}
public String info() {
return "Meeting Name: " + this.meetingName + "\n" +
"Meeting-ID: " + this.meetingID + "\n" +
"Kenncode: " + this.meetingPWD;
}
public Intent share() {
final Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, this.info());
sendIntent.setType("text/plain");
return Intent.createChooser(sendIntent, null);
}
}
@@ -0,0 +1,118 @@
package de.joel.zoomhelper;
import android.content.Intent;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import java.util.ArrayList;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MeetingImportController {
private final MeetingsController meetingsController;
private final MainActivity act;
public MeetingImportController(MeetingsController meetingsController, MainActivity act) {
this.meetingsController = meetingsController;
this.act = act;
}
public void handleSendText(Intent intent) {
final String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
handleSendText(sharedText);
}
public void handleSendText(String sharedText) {
handleSendText(sharedText, false);
}
public boolean handleSendText(String sharedText, boolean checkOnly) {
String meetingID = "";
String meetingPWD = "";
String meetingName = "";
if (sharedText != null) {
Matcher matcher = Pattern.compile("(?i)(?<=(Meeting-ID:)|(Meeting\\sID:)|(ID:)).+").matcher(sharedText);
if (matcher.find()) {
meetingID = Objects.requireNonNull(matcher.group(0)).trim();
}
matcher = Pattern.compile("(?i)(?<=(Kenncode:)|(Passwort:)|(Passcode:)|(Password:)).+").matcher(sharedText);
if (matcher.find()) {
meetingPWD = Objects.requireNonNull(matcher.group(0)).trim();
}
matcher = Pattern.compile("(?i)(?<=(Thema:)|(Meeting\\sName:)|(Topic:)).+").matcher(sharedText);
if (matcher.find()) {
meetingName = Objects.requireNonNull(matcher.group(0)).trim();
}
final AlertDialog.Builder builder = new AlertDialog.Builder(act);
builder.setTitle(R.string.app_name);
builder.setMessage(R.string.meetingAlreadyExists);
if (meetingID.equals("")) {
builder.setMessage(R.string.importError);
builder.setPositiveButton(R.string.ok, (dialog, which) -> dialog.dismiss());
final AlertDialog alert = builder.create();
if (!checkOnly) alert.show();
return false;
} else {
final Meeting mtgToImport = new Meeting(meetingName, meetingID, meetingPWD, "", "");
if (meetingsController.searchMeetingInList(meetingsController.getMeetings(), mtgToImport.getMeetingName()) == -1 && !checkOnly) {
act.fillMeeting(mtgToImport);
} else {
builder.setPositiveButton(R.string.override, (dialog, which) -> {
final int found = meetingsController.searchMeetingInList(meetingsController.getMeetings(), mtgToImport.getMeetingName());
importMeeting(mtgToImport, false);
act.fillMeeting(found);
dialog.dismiss();
});
builder.setNegativeButton(R.string.rename, (dialog, which) -> {
mtgToImport.setMeetingName("");
act.fillMeeting(mtgToImport);
dialog.dismiss();
});
final AlertDialog alert = builder.create();
if (!checkOnly) alert.show();
return true;
}
}
}
return true;
}
public void importMeeting(Meeting meeting, boolean updateAttendee) {
final ArrayList<Meeting> listMeetings = meetingsController.getMeetings();
final int foundCurr = meetingsController.searchMeetingInList(listMeetings, meeting.getMeetingName());
if (foundCurr == -1) {
listMeetings.add(meeting);
if (listMeetings.size() == 1) {
meetingsController.setLastMeeting(0);
} else {
meetingsController.setLastMeeting(listMeetings.size() - 1);
}
} else {
final Meeting currMeeting = meetingsController.getMeetings().get(foundCurr);
currMeeting.setMeetingID(meeting.getMeetingID());
currMeeting.setMeetingPWD(meeting.getMeetingPWD());
if (updateAttendee)
currMeeting.updateAttendee(meeting.getAttendees().get(0).getName(), meeting.getAttendees().get(0).getNum());
listMeetings.set(foundCurr, currMeeting);
meetingsController.setLastMeeting(foundCurr);
}
meetingsController.saveMeetingList(listMeetings);
act.fillDropdownMeetingName();
act.fillDropdownAttendeeName(meetingsController.getLastMeeting());
Toast.makeText(act, R.string.meetingSaved, Toast.LENGTH_SHORT).show();
}
public void importMeeting(Meeting meeting) {
importMeeting(meeting, true);
}
}
@@ -0,0 +1,172 @@
package de.joel.zoomhelper;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Base64;
import androidx.appcompat.app.AlertDialog;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
public class MeetingsController {
private final Context context;
private final Activity activity;
public MeetingsController(Context context) {
this.context = context;
this.activity = (Activity) context;
}
public String meetingsToString(ArrayList<Meeting> listMeetings) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objOutputStream = null;
try {
objOutputStream = new ObjectOutputStream(byteArrayOutputStream);
} catch (IOException e) {
e.printStackTrace();
}
for (Object obj : listMeetings) {
try {
if (objOutputStream == null) throw new AssertionError();
objOutputStream.writeObject(obj);
} catch (IOException e) {
e.printStackTrace();
}
try {
objOutputStream.reset();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
if (objOutputStream == null) throw new AssertionError();
objOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
return Base64.encodeToString(byteArrayOutputStream.toByteArray(), 0);
}
public ArrayList<Meeting> stringToMeetings(String string) {
byte[] bytes = Base64.decode(string, 0);
ArrayList<Meeting> listMeetings = new ArrayList<>();
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream obj = null;
try {
obj = new ObjectInputStream(bis);
} catch (IOException e) {
e.printStackTrace();
}
try {
while (bis.available() != -1) {
//Read object from file
if (obj == null) throw new AssertionError();
Meeting meeting = (Meeting) obj.readObject();
listMeetings.add(meeting);
}
} catch (EOFException ex) {
//ex.printStackTrace();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return listMeetings;
}
@SuppressLint("ApplySharedPref")
public void saveMeetingList(ArrayList<Meeting> meetingList) {
SharedPreferences mPrefs = activity.getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor prefsEditor = mPrefs.edit();
prefsEditor.putString("Meetings", meetingsToString(meetingList));
prefsEditor.commit();
}
@SuppressLint("ApplySharedPref")
public void setLastMeeting(int id) {
SharedPreferences mPrefs = activity.getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor prefsEditor = mPrefs.edit();
prefsEditor.putInt("LastMeeting", id);
prefsEditor.commit();
}
public int getLastMeeting() {
SharedPreferences mPrefs = activity.getPreferences(Context.MODE_PRIVATE);
return mPrefs.getInt("LastMeeting", -1);
}
public ArrayList<Meeting> getMeetings() {
SharedPreferences mPrefs = activity.getPreferences(Context.MODE_PRIVATE);
String value = mPrefs.getString("Meetings", "");
ArrayList<Meeting> meetingList;
if (value.equals("")) {
meetingList = new ArrayList<>();
} else {
meetingList = stringToMeetings(value);
}
return meetingList;
}
public void removeMeeting(int meeting) {
MainActivity act = (MainActivity)context;
ArrayList<Meeting> meetings = getMeetings();
if (meetings.size() > 0 && meeting != -1) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.app_name);
builder.setMessage(context.getString(R.string.suretoremove, meetings.get(meeting).getMeetingName()));
builder.setPositiveButton(R.string.yes, (dialog, which) -> {
dialog.dismiss();
meetings.remove(meeting);
saveMeetingList(meetings);
if (meetings.size() > 1) {
act.fillDropdownMeetingName();
if (getLastMeeting() > meetings.size() - 1) {
setLastMeeting(meetings.size() - 1);
act.fillMeeting(getLastMeeting());
}
} else if (meetings.size() == 1) {
setLastMeeting(0);
act.fillDropdownMeetingName();
act.fillMeeting(getLastMeeting());
} else {
setLastMeeting(-1);
act.fillDropdownMeetingName();
act.fillBlank();
}
});
builder.setNegativeButton(R.string.no, (dialog, which) -> dialog.dismiss());
AlertDialog alert = builder.create();
alert.show();
}
}
public int searchMeetingInList(ArrayList<Meeting> meetings, String name) {
int found = -1;
for (int i = 0; i < meetings.size(); i++) {
if (meetings.get(i).getMeetingName().equals(name)) {
found = i;
break;
}
}
return found;
}
}
@@ -0,0 +1,51 @@
package de.joel.zoomhelper;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import java.util.Objects;
public class ZoomLink {
private final Meeting meeting;
private final Context context;
public ZoomLink(Context context, Meeting meeting) {
this.meeting = meeting;
this.context = context;
}
private String buildZoomURL() {
Uri.Builder builder = new Uri.Builder();
builder.scheme("zoomus")
.authority("zoom.us")
.appendPath("join")
.appendQueryParameter("confno", this.meeting.getMeetingID().replace(" ", ""))
.appendQueryParameter("pwd", this.meeting.getMeetingPWD());
if (!Objects.equals(this.meeting.getAttendees().get(this.meeting.getLastAtt()).getNum(), "")) {
builder.appendQueryParameter("uname", this.meeting.getAttendees().get(this.meeting.getLastAtt()).getName() + " (" + this.meeting.getAttendees().get(this.meeting.getLastAtt()).getNum() + ")");
} else
builder.appendQueryParameter("uname", this.meeting.getAttendees().get(this.meeting.getLastAtt()).getName());
return builder.build().toString();
}
public void launch() {
String url = buildZoomURL();
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
if (intent.resolveActivity(context.getPackageManager()) != null) {
context.startActivity(intent);
}
/*if (testing) {
//For Debugging: Show URL in Alert
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setMessage(url);
dialog.setTitle("Zoom URL (for testing)");
AlertDialog alertDialog = dialog.create();
alertDialog.show();
}*/
}
}
+3 -1
View File
@@ -28,7 +28,7 @@
<string name="alwaysAsk">Dialog immer anzeigen</string> <string name="alwaysAsk">Dialog immer anzeigen</string>
<string name="wantToimportMeeting">Möchtest du das folgende Meeting importieren?</string> <string name="wantToimportMeeting">Möchtest du das folgende Meeting importieren?</string>
<string name="importError">"Dieser Text entspricht keinem für ZoomHelper bekannten Meetingeinladungs-Muster "</string> <string name="importError">"Dieser Text entspricht keinem für ZoomHelper bekannten Meetingeinladungs-Muster "</string>
<string name="ok">ok</string> <string name="ok">OK</string>
<string name="meetingAlreadyExists">Meeting unter diesem Namen existiert schon. Bitte umbenennen, ansonsten wird es beim Speichern überschrieben</string> <string name="meetingAlreadyExists">Meeting unter diesem Namen existiert schon. Bitte umbenennen, ansonsten wird es beim Speichern überschrieben</string>
<string name="override">überschreiben</string> <string name="override">überschreiben</string>
<string name="rename">umbenennen</string> <string name="rename">umbenennen</string>
@@ -39,5 +39,7 @@
<string name="cantCreateShortcut">Du musst das Meeting zunächst speichern, bevor du eine Verknüpfung dazu erstellen kannst</string> <string name="cantCreateShortcut">Du musst das Meeting zunächst speichern, bevor du eine Verknüpfung dazu erstellen kannst</string>
<string name="saveMeeting">Meeting speichern</string> <string name="saveMeeting">Meeting speichern</string>
<string name="cancel">abbrechen</string> <string name="cancel">abbrechen</string>
<string name="meetingSaved">Meeting gespeichert</string>
<string name="meetingWithoutID">Meeting kann nicht ohne ID gespeichert werden!</string>
</resources> </resources>