mirror of
https://github.com/xoureldeen/Vectras-VM-Android.git
synced 2026-04-30 23:39:54 +00:00
V2.1
V2.1 stable
This commit is contained in:
parent
df34043cfe
commit
512140534c
450 changed files with 64666 additions and 0 deletions
235
app/src/main/java/com/vectras/vm/AboutActivity.java
Normal file
235
app/src/main/java/com/vectras/vm/AboutActivity.java
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
package com.vectras.vm;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.google.android.gms.ads.AdRequest;
|
||||
import com.google.android.gms.ads.AdView;
|
||||
import com.google.android.gms.ads.LoadAdError;
|
||||
import com.google.android.gms.ads.MobileAds;
|
||||
import com.google.android.gms.ads.initialization.InitializationStatus;
|
||||
import com.google.android.gms.ads.initialization.OnInitializationCompleteListener;
|
||||
import com.google.android.gms.ads.interstitial.InterstitialAd;
|
||||
import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback;
|
||||
import com.vectras.vm.utils.UIUtils;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.vectras.vm.R;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
public class AboutActivity extends AppCompatActivity implements View.OnClickListener{
|
||||
|
||||
Button btn_osl, btn_clog, btn_youtube, btn_github, btn_telegram, btn_instagram, btn_facebook;
|
||||
String appInfo;
|
||||
|
||||
public String TAG = "AboutActivity";
|
||||
private InterstitialAd mInterstitialAd;
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_about);
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
toolbar.setTitle("About");
|
||||
//btn
|
||||
btn_telegram = (Button) findViewById(R.id.btn_telegram);
|
||||
btn_youtube = (Button) findViewById(R.id.btn_youtube);
|
||||
btn_github = (Button) findViewById(R.id.btn_github);
|
||||
btn_instagram = (Button) findViewById(R.id.btn_instagram);
|
||||
btn_facebook = (Button) findViewById(R.id.btn_facebook);
|
||||
btn_osl = (Button) findViewById(R.id.btn_osl);
|
||||
btn_clog = (Button) findViewById(R.id.btn_changelog);
|
||||
//onclicklistener
|
||||
btn_telegram.setOnClickListener(this);
|
||||
btn_github.setOnClickListener(this);
|
||||
btn_youtube.setOnClickListener(this);
|
||||
btn_instagram.setOnClickListener(this);
|
||||
btn_facebook.setOnClickListener(this);
|
||||
btn_osl.setOnClickListener(this);
|
||||
btn_clog.setOnClickListener(this);
|
||||
|
||||
AdView mAdView = findViewById(R.id.adView);
|
||||
AdRequest adRequest = new AdRequest.Builder().build();
|
||||
mAdView.loadAd(adRequest);
|
||||
new Thread(new Runnable(){
|
||||
|
||||
public void run(){
|
||||
|
||||
BufferedReader reader = null;
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
||||
try {
|
||||
// Create a URL for the desired page
|
||||
URL url = new URL(AppConfig.vectrasInfo); //My text file location
|
||||
//First open the connection
|
||||
HttpURLConnection conn=(HttpURLConnection) url.openConnection();
|
||||
conn.setConnectTimeout(60000); // timing out in a minute
|
||||
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
|
||||
//t=(TextView)findViewById(R.id.TextView1); // ideally do this in onCreate()
|
||||
String str;
|
||||
while ((str = in.readLine()) != null) {
|
||||
builder.append(str);
|
||||
}
|
||||
in.close();
|
||||
} catch (Exception e) {
|
||||
UIUtils.toastLong(AboutActivity.this, "check your internet connection");
|
||||
Log.d("VECTRAS",e.toString());
|
||||
}
|
||||
|
||||
//since we are in background thread, to post results we have to go back to ui thread. do the following for that
|
||||
|
||||
runOnUiThread(new Runnable(){
|
||||
public void run(){
|
||||
appInfo = builder.toString(); // My TextFile has 3 lines
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}).start();
|
||||
FloatingActionButton fab = findViewById(R.id.fab);
|
||||
fab.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Intent i = new Intent(Intent.ACTION_SEND);
|
||||
i.setType("message/rfc822");
|
||||
i.putExtra(Intent.EXTRA_EMAIL , new String[]{"noureldeenelsayed856@gmail.com"});
|
||||
i.putExtra(Intent.EXTRA_SUBJECT, "Vectras User: " + Build.BRAND);
|
||||
i.putExtra(Intent.EXTRA_TEXT , "Device Model: \n" + Build.MODEL + "\n");
|
||||
try {
|
||||
startActivity(Intent.createChooser(i, "Send mail..."));
|
||||
} catch (android.content.ActivityNotFoundException ex) {
|
||||
Snackbar.make(view, "There are no email clients installed.", Snackbar.LENGTH_LONG)
|
||||
.setAction("Action", null).show();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
MobileAds.initialize(this, new OnInitializationCompleteListener() {
|
||||
@Override
|
||||
public void onInitializationComplete(InitializationStatus initializationStatus) {}
|
||||
});
|
||||
InterstitialAd.load(this,"ca-app-pub-3568137780412047/4892595373", adRequest,
|
||||
new InterstitialAdLoadCallback() {
|
||||
@Override
|
||||
public void onAdLoaded(@NonNull InterstitialAd interstitialAd) {
|
||||
// The mInterstitialAd reference will be null until
|
||||
// an ad is loaded.
|
||||
mInterstitialAd = interstitialAd;
|
||||
Log.i(TAG, "onAdLoaded");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {
|
||||
// Handle the error
|
||||
Log.d(TAG, loadAdError.toString());
|
||||
mInterstitialAd = null;
|
||||
}
|
||||
});
|
||||
if (mInterstitialAd != null) {
|
||||
mInterstitialAd.show(AboutActivity.this);
|
||||
} else {
|
||||
Log.d("TAG", "The interstitial ad wasn't ready yet.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if(item.getItemId()== android.R.id.home){
|
||||
finish();
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
public static final int TG = R.id.btn_telegram;
|
||||
public static final int YT = R.id.btn_youtube;
|
||||
public static final int GT = R.id.btn_github;
|
||||
public static final int IG = R.id.btn_instagram;
|
||||
public static final int FB = R.id.btn_facebook;
|
||||
public static final int CL = R.id.btn_changelog;
|
||||
public static final int OSL = R.id.btn_osl;
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int id = v.getId();
|
||||
if (id == TG) {
|
||||
String tg = "https://t.me/vectras_os";
|
||||
Intent f = new Intent(Intent.ACTION_VIEW);
|
||||
f.setData(Uri.parse(tg));
|
||||
startActivity(f);
|
||||
} else if (id == YT) {
|
||||
String tw = "https://www.youtube.com/@XOURELDEEN";
|
||||
Intent w = new Intent(Intent.ACTION_VIEW);
|
||||
w.setData(Uri.parse(tw));
|
||||
startActivity(w);
|
||||
} else if (id == GT) {
|
||||
String gt = AppConfig.vectrasRepo;
|
||||
Intent g = new Intent(Intent.ACTION_VIEW);
|
||||
g.setData(Uri.parse(gt));
|
||||
startActivity(g);
|
||||
} else if (id == IG) {
|
||||
String ig = "https://www.instagram.com/vectrasvm";
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
i.setData(Uri.parse(ig));
|
||||
startActivity(i);
|
||||
} else if (id == FB) {
|
||||
String fb = "https://www.facebook.com/profile.php?id=61555122773211";
|
||||
Intent f = new Intent(Intent.ACTION_VIEW);
|
||||
f.setData(Uri.parse(fb));
|
||||
startActivity(f);
|
||||
} else if (id == CL) {
|
||||
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this, R.style.MainDialogTheme);
|
||||
alertDialogBuilder.setTitle("Changelog");
|
||||
alertDialogBuilder
|
||||
.setMessage(getString(R.string.app_version))
|
||||
.setCancelable(true)
|
||||
.setIcon(R.mipmap.ic_launcher)
|
||||
.setNegativeButton("OK", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
AlertDialog alertDialog = alertDialogBuilder.create();
|
||||
alertDialog.show();
|
||||
} else if (id == OSL) {
|
||||
AlertDialog.Builder alertDialogOSL = new AlertDialog.Builder(this, R.style.MainDialogTheme);
|
||||
alertDialogOSL.setTitle("APP INFO");
|
||||
alertDialogOSL
|
||||
.setMessage(appInfo)
|
||||
.setCancelable(true)
|
||||
.setIcon(R.drawable.round_info_24)
|
||||
.setNegativeButton("OK", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
AlertDialog alertDialogosl = alertDialogOSL.create();
|
||||
alertDialogosl.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
34
app/src/main/java/com/vectras/vm/AppConfig.java
Normal file
34
app/src/main/java/com/vectras/vm/AppConfig.java
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package com.vectras.vm;
|
||||
|
||||
import android.os.Environment;
|
||||
import android.widget.ImageView.ScaleType;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Hashtable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dev
|
||||
*/
|
||||
public class AppConfig {
|
||||
|
||||
// App Config
|
||||
public static final String vectrasWebsite = "https://vectras.netlify.com/";
|
||||
public static final String vectrasRaw = "https://raw.githubusercontent.com/epicstudios856/Vectras-windows-emulator/main/";
|
||||
public static final String vectrasLicense = vectrasRaw + "LICENSE.md";
|
||||
public static final String vectrasPrivacy = vectrasRaw + "PRIVACYANDPOLICY.md";
|
||||
public static final String vectrasTerms = vectrasRaw + "TERMSOFSERVICE.md";
|
||||
public static final String vectrasInfo = vectrasRaw + "info.md";
|
||||
public static final String vectrasRepo = "https://github.com/epicstudios856/Vectras-windows-emulator/tree/main/";
|
||||
public static final String updateJson = vectrasRaw + "UpdateConfig.json";
|
||||
public static final String blogJson = vectrasRaw + "news_list.json";
|
||||
public static final String storeJson = vectrasRaw + "store_list.json";
|
||||
public static final String romsJson = vectrasRaw + "roms.json";
|
||||
|
||||
// App config
|
||||
public static final String datadirpath = SplashActivity.activity.getExternalFilesDir("data")+"/";
|
||||
public static final String sharedFolder = datadirpath + "Vectras/ProgramFiles/";
|
||||
public static final String basefiledir = datadirpath + "Vectras/.qemu/";
|
||||
public static final String maindirpath = datadirpath + "Vectras/";
|
||||
|
||||
}
|
||||
98
app/src/main/java/com/vectras/vm/Blog/AdapterBlog.java
Normal file
98
app/src/main/java/com/vectras/vm/Blog/AdapterBlog.java
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
package com.vectras.vm.Blog;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.vectras.vm.R;
|
||||
import com.vectras.vm.Fragment.HomeFragment;
|
||||
import com.vectras.vm.PostActivity;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import com.vectras.vm.MainActivity;
|
||||
|
||||
public class AdapterBlog extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
|
||||
private Context context;
|
||||
private LayoutInflater inflater;
|
||||
List<DataBlog> data = Collections.emptyList();
|
||||
DataBlog current;
|
||||
int currentPos = 0;
|
||||
|
||||
// create constructor to innitilize context and data sent from MainActivity
|
||||
public AdapterBlog(Context context, List<DataBlog> data) {
|
||||
this.context = context;
|
||||
inflater = LayoutInflater.from(context);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
// Inflate the layout when viewholder created
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = inflater.inflate(R.layout.container_post, parent, false);
|
||||
MyHolder holder = new MyHolder(view);
|
||||
return holder;
|
||||
}
|
||||
|
||||
// Bind data
|
||||
@Override
|
||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
||||
|
||||
// Get current position of item in recyclerview to bind data and assign values from list
|
||||
MyHolder myHolder = (MyHolder) holder;
|
||||
final DataBlog current = data.get(position);
|
||||
myHolder.textTitle.setText(current.postTitle);
|
||||
myHolder.textDate.setText("Date: " + current.postDate);
|
||||
Glide.with(MainActivity.activity).load(current.postThumb).into(myHolder.ivThumb);
|
||||
Animation animation;
|
||||
animation = AnimationUtils.loadAnimation(MainActivity.activity, android.R.anim.slide_in_left);
|
||||
animation.setDuration(300);
|
||||
|
||||
myHolder.cdPost.startAnimation(animation);
|
||||
animation = null;
|
||||
myHolder.cdPost.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View view) {
|
||||
|
||||
PostActivity.title = current.postTitle;
|
||||
PostActivity.content = current.postContent;
|
||||
PostActivity.date = current.postDate;
|
||||
PostActivity.thumb = current.postThumb;
|
||||
MainActivity.activity.startActivity(new Intent(MainActivity.activity, PostActivity.class));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// return total item from List
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
class MyHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
CardView cdPost;
|
||||
TextView textTitle;
|
||||
ImageView ivThumb;
|
||||
TextView textDate;
|
||||
|
||||
// create constructor to get widget reference
|
||||
public MyHolder(View itemView) {
|
||||
super(itemView);
|
||||
cdPost = (CardView) itemView.findViewById(R.id.cdPost);
|
||||
textTitle = (TextView) itemView.findViewById(R.id.textTitle);
|
||||
ivThumb = (ImageView) itemView.findViewById(R.id.ivThumb);
|
||||
textDate = (TextView) itemView.findViewById(R.id.textDate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
9
app/src/main/java/com/vectras/vm/Blog/DataBlog.java
Normal file
9
app/src/main/java/com/vectras/vm/Blog/DataBlog.java
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package com.vectras.vm.Blog;
|
||||
|
||||
public class DataBlog {
|
||||
|
||||
public String postThumb;
|
||||
public String postTitle;
|
||||
public String postContent;
|
||||
public String postDate;
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package com.vectras.vm.Fragment;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
|
||||
import com.vectras.qemu.MainSettingsManager;
|
||||
import com.vectras.vm.R;
|
||||
public class ControlersOptionsFragment extends DialogFragment {
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final Dialog alertDialog = new Dialog(getActivity(), R.style.MainDialogTheme);
|
||||
alertDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
alertDialog.getWindow().setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT));
|
||||
alertDialog.setContentView(R.layout.fragment_controlers_options);
|
||||
alertDialog.findViewById(R.id.gamepadBtn).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
MainSettingsManager.setControlMode(getActivity(), "G");
|
||||
alertDialog.cancel();
|
||||
}
|
||||
});
|
||||
alertDialog.findViewById(R.id.desktopBtn).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
MainSettingsManager.setControlMode(getActivity(), "D");
|
||||
alertDialog.cancel();
|
||||
}
|
||||
});
|
||||
alertDialog.findViewById(R.id.hideBtn).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
MainSettingsManager.setControlMode(getActivity(), "H");
|
||||
alertDialog.cancel();
|
||||
}
|
||||
});
|
||||
alertDialog.show();
|
||||
return alertDialog;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.vectras.vm.Fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.google.android.material.navigation.NavigationView;
|
||||
import com.vectras.vm.R;
|
||||
|
||||
public class DialogSettingsFragment extends Fragment {
|
||||
|
||||
public View view;
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
// Inflate the layout for this fragment
|
||||
view = inflater.inflate(R.layout.content_settings_dialog, container, false);
|
||||
|
||||
NavigationView navView = view.findViewById(R.id.navView);
|
||||
View fragment = view.findViewById(R.id.fragment);
|
||||
navView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
|
||||
|
||||
// This method will trigger on item Click of navigation menu
|
||||
@Override
|
||||
public boolean onNavigationItemSelected(MenuItem menuItem) {
|
||||
//Check to see which item was being clicked and perform appropriate action
|
||||
int id = menuItem.getItemId();
|
||||
if (id == R.id.uiInterface) {
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return view;
|
||||
}
|
||||
}
|
||||
163
app/src/main/java/com/vectras/vm/Fragment/HomeFragment.java
Normal file
163
app/src/main/java/com/vectras/vm/Fragment/HomeFragment.java
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
package com.vectras.vm.Fragment;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.AsyncTask;
|
||||
import android.net.NetworkRequest;
|
||||
import android.os.Bundle;
|
||||
import android.telephony.NetworkScanRequest;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Toast;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.vectras.vm.RomsManagerActivity;
|
||||
import com.vectras.vm.MainRoms.AdapterMainRoms;
|
||||
import com.vectras.vm.MainRoms.DataMainRoms;
|
||||
import com.vectras.vm.R;
|
||||
import com.vectras.vm.Blog.AdapterBlog;
|
||||
import com.vectras.vm.Blog.DataBlog;
|
||||
import com.vectras.vm.AppConfig;
|
||||
import com.vectras.vm.MainActivity;
|
||||
import com.vectras.vm.utils.FileUtils;
|
||||
import com.vectras.vm.utils.UIUtils;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class HomeFragment extends Fragment {
|
||||
|
||||
View view;
|
||||
public static RecyclerView mRVMainRoms;
|
||||
public static LinearLayout romsLayout;
|
||||
public static AdapterMainRoms mMainAdapter;
|
||||
public MainActivity activity;
|
||||
public static JSONArray jArray;
|
||||
public static List<DataMainRoms> data;
|
||||
|
||||
/*private ImageButton mStop;
|
||||
private ImageButton mRestart;*/
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
activity = MainActivity.activity;
|
||||
|
||||
view = inflater.inflate(R.layout.home_fragment, container, false);
|
||||
|
||||
romsLayout = view.findViewById(R.id.romsLayout);
|
||||
|
||||
SwipeRefreshLayout refreshRoms = view.findViewById(R.id.refreshRoms);
|
||||
|
||||
refreshRoms.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
data=new ArrayList<>();
|
||||
|
||||
try {
|
||||
|
||||
jArray = new JSONArray(FileUtils.readFromFile(MainActivity.activity, new File(AppConfig.maindirpath
|
||||
+ "roms-data.json")));
|
||||
|
||||
// Extract data from json and store into ArrayList as class objects
|
||||
for(int i=0;i<jArray.length();i++){
|
||||
JSONObject json_data = jArray.getJSONObject(i);
|
||||
DataMainRoms romsMainData = new DataMainRoms();
|
||||
romsMainData.itemName= json_data.getString("imgName");
|
||||
romsMainData.itemIcon= json_data.getString("imgIcon");
|
||||
romsMainData.itemPath= json_data.getString("imgPath");
|
||||
romsMainData.itemExtra= json_data.getString("imgExtra");
|
||||
data.add(romsMainData);
|
||||
}
|
||||
|
||||
// Setup and Handover data to recyclerview
|
||||
mRVMainRoms = (RecyclerView)view.findViewById(R.id.mRVMainRoms);
|
||||
mMainAdapter = new AdapterMainRoms(MainActivity.activity, data);
|
||||
mRVMainRoms.setAdapter(mMainAdapter);
|
||||
mRVMainRoms.setLayoutManager(new GridLayoutManager(MainActivity.activity, 2));
|
||||
|
||||
} catch (JSONException e) {
|
||||
Toast.makeText(MainActivity.activity, e.toString(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
mMainAdapter.notifyItemRangeChanged(0, mMainAdapter.data.size());
|
||||
refreshRoms.setRefreshing(false);
|
||||
}
|
||||
});
|
||||
data=new ArrayList<>();
|
||||
|
||||
try {
|
||||
|
||||
jArray = new JSONArray(FileUtils.readFromFile(MainActivity.activity, new File(AppConfig.maindirpath
|
||||
+ "roms-data.json")));
|
||||
|
||||
// Extract data from json and store into ArrayList as class objects
|
||||
for(int i=0;i<jArray.length();i++){
|
||||
JSONObject json_data = jArray.getJSONObject(i);
|
||||
DataMainRoms romsMainData = new DataMainRoms();
|
||||
romsMainData.itemName= json_data.getString("imgName");
|
||||
romsMainData.itemIcon= json_data.getString("imgIcon");
|
||||
romsMainData.itemPath= json_data.getString("imgPath");
|
||||
romsMainData.itemExtra= json_data.getString("imgExtra");
|
||||
data.add(romsMainData);
|
||||
}
|
||||
|
||||
// Setup and Handover data to recyclerview
|
||||
mRVMainRoms = (RecyclerView)view.findViewById(R.id.mRVMainRoms);
|
||||
mMainAdapter = new AdapterMainRoms(MainActivity.activity, data);
|
||||
mRVMainRoms.setAdapter(mMainAdapter);
|
||||
mRVMainRoms.setLayoutManager(new GridLayoutManager(MainActivity.activity, 2));
|
||||
|
||||
} catch (JSONException e) {
|
||||
Toast.makeText(MainActivity.activity, e.toString(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* CHECK WHETHER INTERNET CONNECTION IS AVAILABLE OR NOT
|
||||
*/
|
||||
public boolean checkConnection(Context context) {
|
||||
final ConnectivityManager connMgr = (ConnectivityManager) context
|
||||
.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
|
||||
if (connMgr != null) {
|
||||
NetworkInfo activeNetworkInfo = connMgr.getActiveNetworkInfo();
|
||||
|
||||
if (activeNetworkInfo != null) { // connected to the internet
|
||||
// connected to the mobile provider's data plan
|
||||
if (activeNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
|
||||
// connected to wifi
|
||||
return true;
|
||||
} else
|
||||
return activeNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
104
app/src/main/java/com/vectras/vm/Fragment/LoggerFragment.java
Normal file
104
app/src/main/java/com/vectras/vm/Fragment/LoggerFragment.java
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
package com.vectras.vm.Fragment;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemSelectedListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.vectras.vm.R;
|
||||
import com.vectras.vm.RomsManagerActivity;
|
||||
import com.vectras.vm.MainActivity;
|
||||
import com.vectras.vm.adapter.LogsAdapter;
|
||||
import com.vectras.vm.logger.VectrasStatus;
|
||||
import com.vectras.vm.utils.UIUtils;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Objects;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class LoggerFragment extends Fragment {
|
||||
|
||||
View view;
|
||||
MainActivity activity = MainActivity.activity;
|
||||
private final String CREDENTIAL_SHARED_PREF = "settings_prefs";
|
||||
private LogsAdapter mLogAdapter;
|
||||
private RecyclerView logList;
|
||||
private Timer _timer = new Timer();
|
||||
private TimerTask t;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
view = inflater.inflate(R.layout.fragment_logs, container, false);
|
||||
LinearLayoutManager layoutManager = new LinearLayoutManager(MainActivity.activity);
|
||||
mLogAdapter = new LogsAdapter(layoutManager, MainActivity.activity);
|
||||
logList = (RecyclerView) view.findViewById(R.id.recyclerLog);
|
||||
logList.setAdapter(mLogAdapter);
|
||||
logList.setLayoutManager(layoutManager);
|
||||
mLogAdapter.scrollToLastPosition();
|
||||
try {
|
||||
Process process = Runtime.getRuntime().exec("logcat -e");
|
||||
BufferedReader bufferedReader = new BufferedReader(
|
||||
new InputStreamReader(process.getInputStream()));
|
||||
Process process2 = Runtime.getRuntime().exec("logcat -w");
|
||||
BufferedReader bufferedReader2 = new BufferedReader(
|
||||
new InputStreamReader(process2.getInputStream()));
|
||||
|
||||
t = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (bufferedReader.readLine() != null || bufferedReader2.readLine() != null) {
|
||||
String logLine = bufferedReader.readLine();
|
||||
String logLine2 = bufferedReader2.readLine();
|
||||
VectrasStatus.logError("<font color='red'>[E] "+logLine+"</font>");
|
||||
VectrasStatus.logError("<font color='yellow'>[W] "+logLine2+"</font>");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
_timer.scheduleAtFixedRate(t, (int) (0), (int) (100));
|
||||
} catch (IOException e) {
|
||||
Toast.makeText(activity, "There was an error: " + Log.getStackTraceString(e), Toast.LENGTH_LONG).show();
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.vectras.vm.Fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.vectras.vm.R;
|
||||
|
||||
public class UiSettingsFragment extends Fragment {
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
// Inflate the layout for this fragment
|
||||
return inflater.inflate(R.layout.fragment_ui_settings, container, false);
|
||||
}
|
||||
}
|
||||
22
app/src/main/java/com/vectras/vm/ImagePrvActivity.java
Normal file
22
app/src/main/java/com/vectras/vm/ImagePrvActivity.java
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package com.vectras.vm;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.vectras.vm.R;
|
||||
|
||||
public class ImagePrvActivity extends AppCompatActivity {
|
||||
public static String linkIv;
|
||||
public ImageView ivPrv;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.iv_prv);
|
||||
ivPrv = findViewById(R.id.ivPrv);
|
||||
Glide.with(this).load(linkIv).into(ivPrv);
|
||||
}
|
||||
}
|
||||
593
app/src/main/java/com/vectras/vm/MainActivity.java
Normal file
593
app/src/main/java/com/vectras/vm/MainActivity.java
Normal file
|
|
@ -0,0 +1,593 @@
|
|||
package com.vectras.vm;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.Html;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.view.GravityCompat;
|
||||
import androidx.drawerlayout.widget.DrawerLayout;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentPagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.google.android.gms.ads.LoadAdError;
|
||||
import com.google.android.gms.ads.MobileAds;
|
||||
import com.google.android.gms.ads.initialization.InitializationStatus;
|
||||
import com.google.android.gms.ads.initialization.OnInitializationCompleteListener;
|
||||
import com.google.android.gms.ads.interstitial.InterstitialAd;
|
||||
import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback;
|
||||
import com.google.android.material.bottomappbar.BottomAppBar;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.color.DynamicColors;
|
||||
import com.google.android.material.color.utilities.DynamicColor;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.auth.FirebaseUser;
|
||||
import com.google.firebase.database.FirebaseDatabase;
|
||||
import com.vectras.qemu.Config;
|
||||
import com.vectras.qemu.MainActivityCommon;
|
||||
import com.vectras.qemu.MainService;
|
||||
import com.vectras.qemu.MainSettingsManager;
|
||||
import com.vectras.qemu.jni.StartVM;
|
||||
import com.vectras.qemu.utils.FileUtils;
|
||||
import com.vectras.vm.Fragment.HomeFragment;
|
||||
import com.vectras.vm.Fragment.LoggerFragment;
|
||||
import com.vectras.vm.logger.VectrasStatus;
|
||||
import com.vectras.vm.utils.AppUpdater;
|
||||
import com.vectras.qemu.utils.FileInstaller;
|
||||
import com.vectras.qemu.utils.RamInfo;
|
||||
import com.google.android.gms.ads.AdRequest;
|
||||
import com.google.android.gms.ads.AdView;
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.android.material.navigation.NavigationView;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Objects;
|
||||
import java.util.TimerTask;
|
||||
import java.util.Timer;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
private static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
|
||||
public static final String TAG = "Main Activity";
|
||||
// Static
|
||||
public static final String CREDENTIAL_SHARED_PREF = "settings_prefs";
|
||||
|
||||
public static MainActivity activity = null;
|
||||
//private FirebaseAuth mAuth;
|
||||
public DrawerLayout mainDrawer;
|
||||
public Toolbar mainToolbar;
|
||||
public static TextView totalRam;
|
||||
public static TextView usedRam;
|
||||
public static TextView freeRam;
|
||||
public static TextView ipTxt;
|
||||
private Timer _timer = new Timer();
|
||||
private TimerTask t;
|
||||
public ViewPager viewPager;
|
||||
MenuItem prevMenuItem;
|
||||
int pager_number = 2;
|
||||
private InterstitialAd mInterstitialAd;
|
||||
private AdRequest adRequest;
|
||||
public static AppBarLayout appbar;
|
||||
|
||||
public static void UIAlert(String title, String body, Activity activity) {
|
||||
AlertDialog ad;
|
||||
ad = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create();
|
||||
ad.setTitle(title);
|
||||
ad.setMessage(body);
|
||||
ad.setButton(Dialog.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
ad.show();
|
||||
}
|
||||
|
||||
// This is easier: traverse the interfaces and get the local IPs
|
||||
public static String getLocalIpAddress() {
|
||||
try {
|
||||
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
|
||||
NetworkInterface intf = en.nextElement();
|
||||
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
|
||||
InetAddress inetAddress = enumIpAddr.nextElement();
|
||||
if (!inetAddress.isLoopbackAddress() && inetAddress.getHostAddress().toString().contains(".")) {
|
||||
return inetAddress.getHostAddress().toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SocketException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity is first created.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
MainActivityCommon.activity = this;
|
||||
MainActivityCommon.clearNotifications();
|
||||
MainActivityCommon.setupFolders();
|
||||
MainActivityCommon.setupStrictMode();
|
||||
MainActivityCommon.execTimer();
|
||||
MainActivityCommon.checkAndLoadLibs();
|
||||
Config.logFilePath = Config.cacheDir + "/vectras/vectras-log.txt";
|
||||
activity = this;
|
||||
this.setContentView(R.layout.main);
|
||||
this.setupWidgets();
|
||||
initNavigationMenu();
|
||||
FileInstaller.installFiles(activity, false);
|
||||
|
||||
//updateApp(true);
|
||||
//mAuth = FirebaseAuth.getInstance();
|
||||
}
|
||||
|
||||
public static PackageInfo getAppInfo(Context context) {
|
||||
try {
|
||||
return context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateApp(final boolean showDialog) {
|
||||
new AppUpdater(this, new AppUpdater.OnUpdateListener() {
|
||||
@Override
|
||||
public void onUpdateListener(String result) {
|
||||
try {
|
||||
if (!result.contains("Error on getting data")) {
|
||||
final JSONObject obj = new JSONObject(result);
|
||||
PackageInfo pinfo = getAppInfo(getApplicationContext());
|
||||
int versionCode = pinfo.versionCode;
|
||||
if (versionCode < obj.getInt("versionCode")) {
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(activity, R.style.MainDialogTheme);
|
||||
alert.setTitle("Install the latest version")
|
||||
.setMessage(Html.fromHtml(obj.getString("Message") + "<br><br>update size:<br>" + obj.getString("size")))
|
||||
.setCancelable(obj.getBoolean("cancellable"))
|
||||
.setNegativeButton("Update", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
try {
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(obj.getString("url"))));
|
||||
} catch (JSONException e) {
|
||||
|
||||
}
|
||||
}
|
||||
}).show();
|
||||
|
||||
}
|
||||
} else if (result.contains("Error on getting data") && showDialog) {
|
||||
errorUpdateDialog(result);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}).start(showDialog);
|
||||
}
|
||||
|
||||
private void errorUpdateDialog(String error) {
|
||||
VectrasStatus.logInfo(String.format(error));
|
||||
}
|
||||
|
||||
private MenuItem vectrasInfo;
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
getMenuInflater().inflate(R.menu.home_toolbar_menu, menu);
|
||||
vectrasInfo = menu.findItem(R.id.vectrasInfo);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
|
||||
// Menu items
|
||||
int id = item.getItemId();
|
||||
if (id == R.id.vectrasInfo) {
|
||||
appbar = findViewById(R.id.appbar);
|
||||
if (appbar.getTop() < 0)
|
||||
appbar.setExpanded(true);
|
||||
else
|
||||
appbar.setExpanded(false);
|
||||
|
||||
} else if (id == R.id.installRoms) {
|
||||
startActivity(new Intent(MainActivity.activity, RomsManagerActivity.class));
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void initNavigationMenu() {
|
||||
BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.bNav);
|
||||
|
||||
bottomNavigationView
|
||||
.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
|
||||
@Override
|
||||
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
if (id == R.id.menu_home) {
|
||||
viewPager.setCurrentItem(0);
|
||||
} else if (id == R.id.menu_logger) {
|
||||
viewPager.setCurrentItem(1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public class MyAdapter extends FragmentPagerAdapter {
|
||||
|
||||
MyAdapter(FragmentManager fm) {
|
||||
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
|
||||
switch (position) {
|
||||
case 0:
|
||||
return new HomeFragment();
|
||||
case 1:
|
||||
return new LoggerFragment();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return pager_number;
|
||||
}
|
||||
}
|
||||
|
||||
public static LinearLayout extVncLayout;
|
||||
public MaterialButton stopBtn;
|
||||
FirebaseAuth mAuth;
|
||||
FirebaseUser mCurrentUser;
|
||||
|
||||
// Setting up the UI
|
||||
public void setupWidgets() {
|
||||
extVncLayout = findViewById(R.id.extVnc);
|
||||
stopBtn = findViewById(R.id.stopBtn);
|
||||
stopBtn.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
MainActivityCommon.onStopButton(true);
|
||||
}
|
||||
});
|
||||
viewPager = findViewById(R.id.viewPager);
|
||||
viewPager.setAdapter(new MyAdapter(getSupportFragmentManager()));
|
||||
viewPager.setOffscreenPageLimit(pager_number);
|
||||
final BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.bNav);
|
||||
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
if (prevMenuItem != null) {
|
||||
prevMenuItem.setChecked(false);
|
||||
} else {
|
||||
bottomNavigationView.getMenu().getItem(0).setChecked(false);
|
||||
}
|
||||
bottomNavigationView.getMenu().getItem(position).setChecked(true);
|
||||
prevMenuItem = bottomNavigationView.getMenu().getItem(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
appbar = findViewById(R.id.appbar);
|
||||
appbar.setExpanded(false);
|
||||
mainToolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
setSupportActionBar(mainToolbar);
|
||||
mainDrawer = (DrawerLayout) findViewById(R.id.drawer_layout);
|
||||
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, mainDrawer, mainToolbar,
|
||||
R.string.navigation_drawer_open, R.string.navigation_drawer_close);
|
||||
mainDrawer.setDrawerListener(toggle);
|
||||
toggle.syncState();
|
||||
|
||||
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
|
||||
|
||||
View headerView = navigationView.getHeaderView(0);
|
||||
mAuth = FirebaseAuth.getInstance();
|
||||
mCurrentUser = mAuth.getCurrentUser();
|
||||
assert mCurrentUser != null;
|
||||
String name = mCurrentUser.getDisplayName();
|
||||
String email = mCurrentUser.getEmail();
|
||||
Uri picture = mCurrentUser.getPhotoUrl();
|
||||
TextView navUsername = (TextView) headerView.findViewById(R.id.usernameTxt);
|
||||
navUsername.setText(name);
|
||||
TextView navEmail = (TextView) headerView.findViewById(R.id.emailTxt);
|
||||
navEmail.setText(email);
|
||||
TextView viewProfile = (TextView) headerView.findViewById(R.id.viewProfile);
|
||||
|
||||
ImageView ivProfile = (ImageView) headerView.findViewById(R.id.profilePic2);
|
||||
|
||||
viewProfile.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View view) {
|
||||
|
||||
startActivity(new Intent(activity, ProfileActivity.class));
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
//Setting Navigation View Item Selected Listener to handle the item click of the navigation menu
|
||||
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
|
||||
|
||||
// This method will trigger on item Click of navigation menu
|
||||
@Override
|
||||
public boolean onNavigationItemSelected(MenuItem menuItem) {
|
||||
//Closing drawer on item click
|
||||
mainDrawer.closeDrawers();
|
||||
|
||||
//Check to see which item was being clicked and perform appropriate action
|
||||
int id = menuItem.getItemId();
|
||||
if (id == R.id.navigation_item_info) {
|
||||
startActivity(new Intent(activity, AboutActivity.class));
|
||||
} else if (id == R.id.navigation_item_website) {
|
||||
String tw = AppConfig.vectrasWebsite;
|
||||
Intent w = new Intent(Intent.ACTION_VIEW);
|
||||
w.setData(Uri.parse(tw));
|
||||
startActivity(w);
|
||||
} else if (id == R.id.navigation_item_view_logs) {
|
||||
FileUtils.viewVectrasLog(activity);
|
||||
} else if (id == R.id.navigation_item_settings) {
|
||||
startActivity(new Intent(activity, MainSettingsManager.class));
|
||||
} else if (id == R.id.navigation_item_store) {
|
||||
startActivity(new Intent(activity, StoreActivity.class));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
ipTxt = findViewById(R.id.ipTxt);
|
||||
|
||||
ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
|
||||
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
|
||||
activityManager.getMemoryInfo(mi);
|
||||
|
||||
long freeMem = mi.availMem / 1048576L;
|
||||
long totalMem = mi.totalMem / 1048576L;
|
||||
long usedMem = totalMem - freeMem;
|
||||
int freeRamInt = safeLongToInt(freeMem);
|
||||
int totalRamInt = safeLongToInt(totalMem);
|
||||
ipTxt.setText("Local Ip Address: " + getLocalIpAddress());
|
||||
|
||||
SharedPreferences credentials = activity.getSharedPreferences(CREDENTIAL_SHARED_PREF, Context.MODE_PRIVATE);
|
||||
|
||||
totalRam = findViewById(R.id.totalRam);
|
||||
usedRam = findViewById(R.id.usedRam);
|
||||
freeRam = findViewById(R.id.freeRam);
|
||||
|
||||
ipTxt.setVisibility(View.GONE);
|
||||
String vectrasMemory = String.valueOf(RamInfo.vectrasMemory());
|
||||
t = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ActivityManager.MemoryInfo miI = new ActivityManager.MemoryInfo();
|
||||
ActivityManager activityManagerr = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
|
||||
activityManagerr.getMemoryInfo(miI);
|
||||
long freeMemory = miI.availMem / 1048576L;
|
||||
long totalMemory = miI.totalMem / 1048576L;
|
||||
long usedMemory = totalMemory - freeMemory;
|
||||
|
||||
totalRam.setText("Total Memory: " + totalMemory + " MB");
|
||||
usedRam.setText("Used Memory: " + usedMemory + " MB");
|
||||
freeRam.setText("Free Memory: " + freeMemory + " MB (" + vectrasMemory + " used)");
|
||||
ProgressBar progressBar = findViewById(R.id.progressBar);
|
||||
progressBar.setMax((int) totalMemory);
|
||||
if (SDK_INT >= Build.VERSION_CODES.N) {
|
||||
progressBar.setProgress((int) usedMemory, true);
|
||||
} else {
|
||||
progressBar.setProgress((int) usedMemory);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
_timer.scheduleAtFixedRate(t, (int) (0), (int) (1000));
|
||||
|
||||
AdView mAdView = findViewById(R.id.adView);
|
||||
adRequest = new AdRequest.Builder().build();
|
||||
mAdView.loadAd(adRequest);
|
||||
|
||||
MobileAds.initialize(this, new OnInitializationCompleteListener() {
|
||||
@Override
|
||||
public void onInitializationComplete(InitializationStatus initializationStatus) {
|
||||
}
|
||||
});
|
||||
if (MainSettingsManager.getPromptUpdateVersion(activity))
|
||||
updateApp(true);
|
||||
/*FirebaseUser user = mAuth.getCurrentUser();
|
||||
TextView usernameTxt = findViewById(R.id.usernameTxt);
|
||||
TextView emailTxt = findViewById(R.id.emailTxt);
|
||||
ImageView profilePic = findViewById(R.id.profilePic);
|
||||
if (user != null) {
|
||||
// Name, email address, and profile photo Url
|
||||
String name = user.getDisplayName();
|
||||
String email = user.getEmail();
|
||||
Uri photoUrl = user.getPhotoUrl();
|
||||
|
||||
// Check if user's email is verified
|
||||
boolean emailVerified = user.isEmailVerified();
|
||||
|
||||
// The user's ID, unique to the Firebase project. Do NOT use this value to
|
||||
// authenticate with your backend server, if you have one. Use
|
||||
// FirebaseUser.getIdToken() instead.
|
||||
String uid = user.getUid();
|
||||
|
||||
usernameTxt.setText(name);
|
||||
emailTxt.setText(email);
|
||||
if (photoUrl != null)
|
||||
Glide.with(activity).load(photoUrl.toString()).into(profilePic);
|
||||
}*/
|
||||
}
|
||||
|
||||
public static int safeLongToInt(long l) {
|
||||
if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException(l + " cannot be cast to int without changing its value.");
|
||||
}
|
||||
return (int) l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
|
||||
if (drawer.isDrawerOpen(GravityCompat.START)) {
|
||||
drawer.closeDrawer(GravityCompat.START);
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
private void goToURL(String url) {
|
||||
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
i.setData(Uri.parse(url));
|
||||
activity.startActivity(i);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
MainActivityCommon.stopTimeListener();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
InterstitialAd.load(this, "ca-app-pub-3568137780412047/7745973511", adRequest,
|
||||
new InterstitialAdLoadCallback() {
|
||||
@Override
|
||||
public void onAdLoaded(@NonNull InterstitialAd interstitialAd) {
|
||||
// The mInterstitialAd reference will be null until
|
||||
// an ad is loaded.
|
||||
mInterstitialAd = interstitialAd;
|
||||
Log.i(TAG, "onAdLoaded");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {
|
||||
// Handle the error
|
||||
Log.d(TAG, loadAdError.toString());
|
||||
mInterstitialAd = null;
|
||||
}
|
||||
});
|
||||
if (mInterstitialAd != null) {
|
||||
mInterstitialAd.show(MainActivity.this);
|
||||
} else {
|
||||
Log.d("TAG", "The interstitial ad wasn't ready yet.");
|
||||
}
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
MainActivityCommon.stopTimeListener();
|
||||
}
|
||||
|
||||
public boolean loaded = false;
|
||||
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
assert mCurrentUser != null;
|
||||
String name = mCurrentUser.getDisplayName();
|
||||
String email = mCurrentUser.getEmail();
|
||||
Uri picture = mCurrentUser.getPhotoUrl();
|
||||
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
|
||||
View headerView = navigationView.getHeaderView(0);
|
||||
TextView navUsername = (TextView) headerView.findViewById(R.id.usernameTxt);
|
||||
navUsername.setText(name);
|
||||
TextView navEmail = (TextView) headerView.findViewById(R.id.emailTxt);
|
||||
navEmail.setText(email);
|
||||
ImageView ivProfile = (ImageView) headerView.findViewById(R.id.profilePic2);
|
||||
if (MainService.isRunning && Objects.equals(Config.ui, "VNC")) {
|
||||
MainActivityCommon.startvnc();
|
||||
}
|
||||
MainActivityCommon.execTimer();
|
||||
|
||||
InterstitialAd.load(this, "ca-app-pub-3568137780412047/7745973511", adRequest,
|
||||
new InterstitialAdLoadCallback() {
|
||||
@Override
|
||||
public void onAdLoaded(@NonNull InterstitialAd interstitialAd) {
|
||||
// The mInterstitialAd reference will be null until
|
||||
// an ad is loaded.
|
||||
mInterstitialAd = interstitialAd;
|
||||
Log.i(TAG, "onAdLoaded");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {
|
||||
// Handle the error
|
||||
Log.d(TAG, loadAdError.toString());
|
||||
mInterstitialAd = null;
|
||||
}
|
||||
});
|
||||
if (mInterstitialAd != null && !loaded && !MainService.isRunning) {
|
||||
mInterstitialAd.show(MainActivity.this);
|
||||
loaded = true;
|
||||
} else {
|
||||
Log.d("TAG", "The interstitial ad wasn't ready yet.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
190
app/src/main/java/com/vectras/vm/MainRoms/AdapterMainRoms.java
Normal file
190
app/src/main/java/com/vectras/vm/MainRoms/AdapterMainRoms.java
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
package com.vectras.vm.MainRoms;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.vectras.qemu.Config;
|
||||
import com.vectras.qemu.MainActivityCommon;
|
||||
import com.vectras.qemu.MainService;
|
||||
import com.vectras.qemu.MainSettingsManager;
|
||||
import com.vectras.qemu.jni.StartVM;
|
||||
import com.vectras.qemu.utils.Machine;
|
||||
import com.vectras.vm.AppConfig;
|
||||
import com.vectras.vm.Fragment.HomeFragment;
|
||||
import com.vectras.vm.MainActivity;
|
||||
import com.vectras.vm.R;
|
||||
import com.vectras.vm.utils.UIUtils;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class AdapterMainRoms extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
|
||||
private Context context;
|
||||
private LayoutInflater inflater;
|
||||
public List<DataMainRoms> data = Collections.emptyList();
|
||||
int currentPos = 0;
|
||||
private int mSelectedItem = -1;
|
||||
|
||||
// create constructor to innitilize context and data sent from MainActivity
|
||||
public AdapterMainRoms(Context context, List<DataMainRoms> data) {
|
||||
this.context = context;
|
||||
inflater = LayoutInflater.from(context);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
// Inflate the layout when viewholder created
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = inflater.inflate(R.layout.container_main_roms, parent, false);
|
||||
MyHolder holder = new MyHolder(view);
|
||||
return holder;
|
||||
}
|
||||
|
||||
// Bind data
|
||||
@Override
|
||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
|
||||
|
||||
// Get current position of item in recyclerview to bind data and assign values from list
|
||||
final MyHolder myHolder = (MyHolder) holder;
|
||||
final DataMainRoms current = data.get(position);
|
||||
myHolder.textName.setText(current.itemName);
|
||||
Bitmap bmImg = BitmapFactory.decodeFile(current.itemIcon);
|
||||
myHolder.ivIcon.setImageBitmap(bmImg);
|
||||
|
||||
myHolder.cdRoms.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View view) {
|
||||
MainActivityCommon.setupNativeLibs();
|
||||
Config.hda_path = current.itemPath;
|
||||
Config.extra_params = current.itemExtra;
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
public void run() {
|
||||
if (!Config.loadNativeLibsEarly && !Config.loadNativeLibsMainThread) {
|
||||
MainActivityCommon.setupNativeLibs();
|
||||
}
|
||||
MainActivityCommon.onStartButton();
|
||||
}
|
||||
});
|
||||
thread.setPriority(Thread.MIN_PRIORITY);
|
||||
thread.start();
|
||||
|
||||
}
|
||||
});
|
||||
myHolder.cdRoms.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
showDialog(current.itemName, current.itemPath, current.itemIcon);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showDialog(String title, String path, String pathIcon) {
|
||||
|
||||
final Dialog dialog = new Dialog(MainActivity.activity, R.style.MainDialogTheme);
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
dialog.setContentView(R.layout.rom_options_layout);
|
||||
|
||||
LinearLayout removeLayout = dialog.findViewById(R.id.layoutRemove);
|
||||
|
||||
removeLayout.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
AlertDialog ad;
|
||||
ad = new AlertDialog.Builder(MainActivity.activity, R.style.MainDialogTheme).create();
|
||||
ad.setTitle("Remove " + title);
|
||||
ad.setMessage("Are you sure?");
|
||||
ad.setButton(Dialog.BUTTON_NEGATIVE, "REMOVE " + title, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
File file = new File(path);
|
||||
file.delete();
|
||||
File fileIcon = new File(pathIcon);
|
||||
fileIcon.delete();
|
||||
|
||||
HomeFragment.mMainAdapter = new AdapterMainRoms(MainActivity.activity, HomeFragment.data);
|
||||
HomeFragment.data.remove(currentPos);
|
||||
HomeFragment.mRVMainRoms.setAdapter(HomeFragment.mMainAdapter);
|
||||
HomeFragment.mRVMainRoms.setLayoutManager(new GridLayoutManager(MainActivity.activity, 2));
|
||||
HomeFragment.jArray.remove(currentPos);
|
||||
try {
|
||||
Writer output = null;
|
||||
File jsonFile = new File(AppConfig.maindirpath + "roms-data" + ".json");
|
||||
output = new BufferedWriter(new FileWriter(jsonFile));
|
||||
output.write(HomeFragment.jArray.toString().replace("\\", "").replace("//", "/"));
|
||||
output.close();
|
||||
} catch (Exception e) {
|
||||
UIUtils.toastLong(MainActivity.activity, e.toString());
|
||||
}
|
||||
UIUtils.toastLong(MainActivity.activity, title + " are removed successfully!");
|
||||
return;
|
||||
}
|
||||
});
|
||||
ad.setButton(Dialog.BUTTON_POSITIVE, "CANCEL", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
ad.show();
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
dialog.getWindow().getAttributes().windowAnimations = R.style.DialogAnimation;
|
||||
dialog.getWindow().setGravity(Gravity.BOTTOM);
|
||||
|
||||
}
|
||||
|
||||
// return total item from List
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
class MyHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
CardView cdRoms;
|
||||
TextView textName;
|
||||
ImageView ivIcon;
|
||||
|
||||
// create constructor to get widget reference
|
||||
public MyHolder(View itemView) {
|
||||
super(itemView);
|
||||
cdRoms = (CardView) itemView.findViewById(R.id.cdItem);
|
||||
textName = (TextView) itemView.findViewById(R.id.textName);
|
||||
ivIcon = (ImageView) itemView.findViewById(R.id.ivIcon);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.vectras.vm.MainRoms;
|
||||
public class DataMainRoms {
|
||||
public String itemIcon;
|
||||
public String itemName;
|
||||
public String itemPath;
|
||||
public String itemExtra;
|
||||
}
|
||||
150
app/src/main/java/com/vectras/vm/PostActivity.java
Normal file
150
app/src/main/java/com/vectras/vm/PostActivity.java
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
package com.vectras.vm;
|
||||
|
||||
import android.text.Html;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.google.android.gms.ads.AdRequest;
|
||||
import com.google.android.gms.ads.AdView;
|
||||
import com.google.android.gms.ads.LoadAdError;
|
||||
import com.google.android.gms.ads.MobileAds;
|
||||
import com.google.android.gms.ads.initialization.InitializationStatus;
|
||||
import com.google.android.gms.ads.initialization.OnInitializationCompleteListener;
|
||||
import com.google.android.gms.ads.interstitial.InterstitialAd;
|
||||
import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback;
|
||||
import com.vectras.vm.R;
|
||||
import com.vectras.vm.utils.UIUtils;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
public class PostActivity extends AppCompatActivity {
|
||||
|
||||
private Toolbar tb;
|
||||
public static TextView postTitle;
|
||||
public static TextView postContent;
|
||||
public static TextView postDate;
|
||||
public static ImageView postThumb;
|
||||
public static String title, content, contentStr, date, thumb;
|
||||
|
||||
private InterstitialAd mInterstitialAd;
|
||||
private String TAG = "PostActivity";
|
||||
|
||||
/**
|
||||
* Called when the activity is first created.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
this.setContentView(R.layout.post_content);
|
||||
postTitle = findViewById(R.id.postTitle);
|
||||
postContent = findViewById(R.id.postContent);
|
||||
postDate = findViewById(R.id.postDate);
|
||||
postThumb = findViewById(R.id.postThumb);
|
||||
tb = (Toolbar) findViewById(R.id.toolbar);
|
||||
setSupportActionBar(tb);
|
||||
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
|
||||
AdView mAdView = findViewById(R.id.adView);
|
||||
AdRequest adRequest = new AdRequest.Builder().build();
|
||||
mAdView.loadAd(adRequest);
|
||||
MobileAds.initialize(this, new OnInitializationCompleteListener() {
|
||||
@Override
|
||||
public void onInitializationComplete(InitializationStatus initializationStatus) {
|
||||
}
|
||||
});
|
||||
InterstitialAd.load(this, "ca-app-pub-3568137780412047/7745973511", adRequest,
|
||||
new InterstitialAdLoadCallback() {
|
||||
@Override
|
||||
public void onAdLoaded(@NonNull InterstitialAd interstitialAd) {
|
||||
// The mInterstitialAd reference will be null until
|
||||
// an ad is loaded.
|
||||
mInterstitialAd = interstitialAd;
|
||||
Log.i(TAG, "onAdLoaded");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {
|
||||
// Handle the error
|
||||
Log.d(TAG, loadAdError.toString());
|
||||
mInterstitialAd = null;
|
||||
}
|
||||
});
|
||||
postContent.setTextIsSelectable(true);
|
||||
|
||||
Glide.with(this).load(thumb).into(postThumb);
|
||||
new Thread(new Runnable() {
|
||||
|
||||
public void run() {
|
||||
|
||||
BufferedReader reader = null;
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
||||
try {
|
||||
// Create a URL for the desired page
|
||||
URL url = new URL(content); //My text file location
|
||||
//First open the connection
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setConnectTimeout(60000); // timing out in a minute
|
||||
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
|
||||
//t=(TextView)findViewById(R.id.TextView1); // ideally do this in onCreate()
|
||||
String str;
|
||||
while ((str = in.readLine()) != null) {
|
||||
builder.append(str);
|
||||
}
|
||||
in.close();
|
||||
} catch (Exception e) {
|
||||
postContent.setText("no internet connection");
|
||||
UIUtils.toastLong(PostActivity.this, "check your internet connection");
|
||||
Log.d("VECTRAS", e.toString());
|
||||
}
|
||||
|
||||
//since we are in background thread, to post results we have to go back to ui thread. do the following for that
|
||||
|
||||
PostActivity.this.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
contentStr = builder.toString(); // My TextFile has 3 lines
|
||||
postContent.setText(Html.fromHtml(contentStr));
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}).start();
|
||||
postDate.setText(date);
|
||||
postTitle.setText(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
finish();
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
super.onBackPressed();
|
||||
finish();
|
||||
}
|
||||
|
||||
}
|
||||
195
app/src/main/java/com/vectras/vm/ProfileActivity.java
Normal file
195
app/src/main/java/com/vectras/vm/ProfileActivity.java
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
package com.vectras.vm;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.google.android.gms.tasks.Continuation;
|
||||
import com.google.android.gms.tasks.OnCompleteListener;
|
||||
import com.google.android.gms.tasks.OnSuccessListener;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.auth.FirebaseUser;
|
||||
import com.google.firebase.auth.UserProfileChangeRequest;
|
||||
import com.google.firebase.database.DatabaseReference;
|
||||
import com.google.firebase.database.FirebaseDatabase;
|
||||
import com.google.firebase.storage.FirebaseStorage;
|
||||
import com.google.firebase.storage.StorageReference;
|
||||
import com.google.firebase.storage.UploadTask;
|
||||
import com.vectras.qemu.MainService;
|
||||
import com.vectras.vm.ui.login.SignupActivity;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
public class ProfileActivity extends AppCompatActivity {
|
||||
|
||||
private ProfileActivity activity;
|
||||
public TextInputEditText profileUsername;
|
||||
public MaterialButton saveBtn;
|
||||
FirebaseAuth mAuth;
|
||||
FirebaseUser mCurrentUser;
|
||||
private DatabaseReference newUser;
|
||||
public Uri profileUri;
|
||||
|
||||
public ImageView profilePic;
|
||||
|
||||
public ProgressBar loadingPb;
|
||||
|
||||
private Uri imgUri = null;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_profile);
|
||||
activity = this;
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
mAuth = FirebaseAuth.getInstance();
|
||||
mCurrentUser = mAuth.getCurrentUser();
|
||||
newUser = FirebaseDatabase.getInstance().getReference().child(mCurrentUser.getUid());
|
||||
newUser.child("email").setValue(mCurrentUser.getEmail());
|
||||
String name = mCurrentUser.getDisplayName();
|
||||
String email = mCurrentUser.getEmail();
|
||||
Uri picture = mCurrentUser.getPhotoUrl();
|
||||
profilePic = findViewById(R.id.profilePic);
|
||||
profileUsername = findViewById(R.id.profileName);
|
||||
saveBtn = findViewById(R.id.saveBtn);
|
||||
loadingPb = findViewById(R.id.loadingPb);
|
||||
//Glide.with(activity).load(picture).error(R.drawable.person_24).into(profilePic);
|
||||
profileUsername.setText(name);
|
||||
if (profileUsername.getText().toString().equals(mCurrentUser.getDisplayName())) {
|
||||
saveBtn.setEnabled(false);
|
||||
} else {
|
||||
saveBtn.setEnabled(true);
|
||||
}
|
||||
TextWatcher afterTextChangedListener = new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (profileUsername.getText().toString().trim().length() > 0) {
|
||||
if (profileUsername.getText().toString().equals(mCurrentUser.getDisplayName())) {
|
||||
saveBtn.setEnabled(false);
|
||||
} else {
|
||||
saveBtn.setEnabled(true);
|
||||
}
|
||||
} else {
|
||||
saveBtn.setEnabled(false);
|
||||
profileUsername.setError("username can't be empty!");
|
||||
}
|
||||
}
|
||||
};
|
||||
profilePic.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent i = new Intent(Intent.ACTION_PICK);
|
||||
i.setType("image/*");
|
||||
startActivityForResult(i, 1009);
|
||||
}
|
||||
});
|
||||
profileUsername.addTextChangedListener(afterTextChangedListener);
|
||||
saveBtn.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View view) {
|
||||
loadingPb.setVisibility(View.VISIBLE);
|
||||
|
||||
UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder()
|
||||
.setDisplayName(String.valueOf(profileUsername.getText()))
|
||||
.setPhotoUri(imgUri)
|
||||
.build();
|
||||
|
||||
mCurrentUser.updateProfile(profileUpdates)
|
||||
.addOnCompleteListener(new OnCompleteListener<Void>() {
|
||||
@Override
|
||||
public void onComplete(@NonNull Task<Void> task) {
|
||||
if (task.isSuccessful()) {
|
||||
mCurrentUser.sendEmailVerification()
|
||||
.addOnCompleteListener(new OnCompleteListener<Void>() {
|
||||
@Override
|
||||
public void onComplete(@NonNull Task<Void> task) {
|
||||
saveBtn.setEnabled(false);
|
||||
loadingPb.setVisibility(View.GONE);
|
||||
View rootView = findViewById(R.id.main_layout);
|
||||
Snackbar.make(rootView, "Updated Successfully!", 3000).show();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == 1009 && resultCode == RESULT_OK) {
|
||||
imgUri = data.getData();
|
||||
Glide.with(activity).load(imgUri).into(profilePic);
|
||||
saveBtn.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
finish();
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
public void onBackPressed() {
|
||||
finish();
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (profileUsername.getText().toString().equals(mCurrentUser.getDisplayName())) {
|
||||
saveBtn.setEnabled(false);
|
||||
} else {
|
||||
saveBtn.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
if (profileUsername.getText().toString().equals(mCurrentUser.getDisplayName())) {
|
||||
saveBtn.setEnabled(false);
|
||||
} else {
|
||||
saveBtn.setEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
128
app/src/main/java/com/vectras/vm/Roms/AdapterRoms.java
Normal file
128
app/src/main/java/com/vectras/vm/Roms/AdapterRoms.java
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
package com.vectras.vm.Roms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.graphics.Color;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.webkit.URLUtil;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.vectras.vm.AppConfig;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.vectras.vm.RomsManagerActivity;
|
||||
import com.vectras.vm.MainActivity;
|
||||
import com.vectras.vm.R;
|
||||
import com.vectras.vm.utils.FileUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.app.Dialog;
|
||||
|
||||
public class AdapterRoms extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
|
||||
private Context context;
|
||||
private LayoutInflater inflater;
|
||||
static List<DataRoms> data = Collections.emptyList();
|
||||
static List<DataRoms> filteredData = Collections.emptyList();
|
||||
DataRoms current;
|
||||
int currentPos = 0;
|
||||
private int mSelectedItem = -1;
|
||||
|
||||
// create constructor to innitilize context and data sent from MainActivity
|
||||
public AdapterRoms(Context context, List<DataRoms> data) {
|
||||
this.context = context;
|
||||
inflater = LayoutInflater.from(context);
|
||||
AdapterRoms.data = data;
|
||||
AdapterRoms.filteredData = data;
|
||||
}
|
||||
|
||||
// Inflate the layout when viewholder created
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = inflater.inflate(R.layout.container_roms, parent, false);
|
||||
MyHolder holder = new MyHolder(view);
|
||||
return holder;
|
||||
}
|
||||
|
||||
// Bind data
|
||||
@Override
|
||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
|
||||
|
||||
// Get current position of item in recyclerview to bind data and assign values from list
|
||||
final MyHolder myHolder = (MyHolder) holder;
|
||||
final DataRoms current = data.get(position);
|
||||
Glide.with(RomsManagerActivity.activity).load(current.itemIcon).into(myHolder.ivIcon);
|
||||
myHolder.textName.setText(current.itemName + " " + current.itemArch);
|
||||
myHolder.textSize.setText(current.itemSize);
|
||||
myHolder.checkBox.setChecked(position == mSelectedItem);
|
||||
if (current.itemAvail) {
|
||||
myHolder.textAvail.setText("availability: available");
|
||||
myHolder.textAvail.setTextColor(Color.GREEN);
|
||||
} else if (!current.itemAvail) {
|
||||
myHolder.textAvail.setText("availability: unavailable");
|
||||
myHolder.textAvail.setTextColor(Color.RED);
|
||||
myHolder.checkBox.setEnabled(false);
|
||||
}
|
||||
if (current.itemAvail)
|
||||
myHolder.checkBox.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mSelectedItem = position;
|
||||
notifyItemRangeChanged(0, data.size());
|
||||
RomsManagerActivity.selected = true;
|
||||
RomsManagerActivity.selectedPath = current.itemPath;
|
||||
RomsManagerActivity.selectedExtra = current.itemExtra;
|
||||
RomsManagerActivity.selectedName = current.itemName + " " + current.itemArch;
|
||||
RomsManagerActivity.selectedLink = current.itemUrl;
|
||||
RomsManagerActivity.selectedIcon = current.itemIcon;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// return total item from List
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
class MyHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
TextView textName, textAvail, textSize;
|
||||
ImageView ivIcon;
|
||||
|
||||
RadioButton checkBox;
|
||||
|
||||
// create constructor to get widget reference
|
||||
public MyHolder(View itemView) {
|
||||
super(itemView);
|
||||
textName = (TextView) itemView.findViewById(R.id.textName);
|
||||
ivIcon = (ImageView) itemView.findViewById(R.id.ivIcon);
|
||||
textSize = (TextView) itemView.findViewById(R.id.textSize);
|
||||
textAvail = (TextView) itemView.findViewById(R.id.textAvail);
|
||||
|
||||
checkBox = (RadioButton) itemView.findViewById(R.id.checkBox);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
14
app/src/main/java/com/vectras/vm/Roms/DataRoms.java
Normal file
14
app/src/main/java/com/vectras/vm/Roms/DataRoms.java
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package com.vectras.vm.Roms;
|
||||
|
||||
public class DataRoms {
|
||||
|
||||
public String itemIcon;
|
||||
public String itemName;
|
||||
public String itemArch;
|
||||
public String itemKernel;
|
||||
public Boolean itemAvail;
|
||||
public String itemSize;
|
||||
public String itemUrl;
|
||||
public String itemPath;
|
||||
public String itemExtra;
|
||||
}
|
||||
646
app/src/main/java/com/vectras/vm/RomsManagerActivity.java
Normal file
646
app/src/main/java/com/vectras/vm/RomsManagerActivity.java
Normal file
|
|
@ -0,0 +1,646 @@
|
|||
package com.vectras.vm;
|
||||
|
||||
import static android.content.Intent.ACTION_OPEN_DOCUMENT;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.MediaStore;
|
||||
import android.text.Html;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.webkit.URLUtil;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.button.MaterialButtonToggleGroup;
|
||||
import com.vectras.vm.AppConfig;
|
||||
import com.vectras.vm.MainRoms.AdapterMainRoms;
|
||||
import com.vectras.vm.MainRoms.DataMainRoms;
|
||||
import com.vectras.vm.Roms.AdapterRoms;
|
||||
import com.vectras.vm.Roms.DataRoms;
|
||||
import com.vectras.vm.logger.VectrasStatus;
|
||||
import com.vectras.qemu.utils.FileInstaller;
|
||||
import com.vectras.vm.utils.FileUtils;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
|
||||
import com.vectras.vm.utils.UIUtils;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileWriter;
|
||||
import java.io.InputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Writer;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class RomsManagerActivity extends AppCompatActivity {
|
||||
public static RomsManagerActivity activity;
|
||||
|
||||
public static MaterialButton goBtn;
|
||||
|
||||
public static CheckBox acceptLiceneseChkBox;
|
||||
public static AlertDialog ad;
|
||||
|
||||
public static String license;
|
||||
public static RecyclerView mRVRoms;
|
||||
public static AdapterRoms mAdapter;
|
||||
public static String Data;
|
||||
public static List<DataRoms> data;
|
||||
public static Boolean selected = false;
|
||||
public static String selectedPath = null;
|
||||
public static String selectedExtra = null;
|
||||
public static String selectedLink = null;
|
||||
public static String selectedName = null;
|
||||
public static String selectedIcon = null;
|
||||
|
||||
public MaterialButtonToggleGroup filterToggle;
|
||||
public MaterialButton windowsToggle;
|
||||
public MaterialButton linuxToggle;
|
||||
public MaterialButton appleToggle;
|
||||
public MaterialButton androidToggle;
|
||||
public MaterialButton otherToggle;
|
||||
|
||||
/**
|
||||
* Called when the activity is first created.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
activity = this;
|
||||
setContentView(R.layout.activity_roms_manager);
|
||||
filterToggle = findViewById(R.id.filterToggle);
|
||||
windowsToggle = findViewById(R.id.windowsToggle);
|
||||
linuxToggle = findViewById(R.id.linuxToggle);
|
||||
appleToggle = findViewById(R.id.appleToggle);
|
||||
androidToggle = findViewById(R.id.androidToggle);
|
||||
otherToggle = findViewById(R.id.otherToggle);
|
||||
mRVRoms = findViewById(R.id.romsRv);
|
||||
filterToggle.addOnButtonCheckedListener(new MaterialButtonToggleGroup.OnButtonCheckedListener() {
|
||||
@Override
|
||||
public void onButtonChecked(MaterialButtonToggleGroup group, int checkedId, boolean isChecked) {
|
||||
if (checkedId == R.id.windowsToggle) {
|
||||
if (isChecked)
|
||||
filter = "windows";
|
||||
else
|
||||
filter = null;
|
||||
} else if (checkedId == R.id.linuxToggle) {
|
||||
if (isChecked)
|
||||
filter = "linux";
|
||||
else
|
||||
filter = null;
|
||||
} else if (checkedId == R.id.appleToggle) {
|
||||
if (isChecked)
|
||||
filter = "apple";
|
||||
else
|
||||
filter = null;
|
||||
} else if (checkedId == R.id.androidToggle) {
|
||||
if (isChecked)
|
||||
filter = "android";
|
||||
else
|
||||
filter = null;
|
||||
} else if (checkedId == R.id.otherToggle) {
|
||||
if (isChecked)
|
||||
filter = "other";
|
||||
else
|
||||
filter = null;
|
||||
}
|
||||
new RomsManagerActivity.AsyncLogin().execute();
|
||||
}
|
||||
});
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
|
||||
new RomsManagerActivity.AsyncLogin().execute();
|
||||
new Thread(new Runnable() {
|
||||
|
||||
public void run() {
|
||||
|
||||
BufferedReader reader = null;
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
||||
try {
|
||||
// Create a URL for the desired page
|
||||
URL url = new URL(AppConfig.vectrasTerms); //My text file location
|
||||
//First open the connection
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setConnectTimeout(60000); // timing out in a minute
|
||||
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
|
||||
//t=(TextView)findViewById(R.id.TextView1); // ideally do this in onCreate()
|
||||
String str;
|
||||
while ((str = in.readLine()) != null) {
|
||||
builder.append(str);
|
||||
}
|
||||
in.close();
|
||||
} catch (Exception e) {
|
||||
acceptLiceneseChkBox.setEnabled(false);
|
||||
UIUtils.toastLong(activity, "no internet connection "+e.toString());
|
||||
}
|
||||
|
||||
//since we are in background thread, to post results we have to go back to ui thread. do the following for that
|
||||
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
license = builder.toString(); // My TextFile has 3 lines
|
||||
acceptLiceneseChkBox.setEnabled(true);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}).start();
|
||||
|
||||
acceptLiceneseChkBox = findViewById(R.id.acceptLiceneseChkBox);
|
||||
|
||||
acceptLiceneseChkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
if (isChecked) {
|
||||
UIAlertLicense("Terms&Conditions", license, activity);
|
||||
} else {
|
||||
goBtn.setEnabled(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
goBtn = (MaterialButton) findViewById(R.id.goBtn);
|
||||
|
||||
goBtn.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
onFirstStartup();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public static void UIAlertLicense(String title, String html, final Activity activity) {
|
||||
AlertDialog alertDialog;
|
||||
alertDialog = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create();
|
||||
alertDialog.setTitle(title);
|
||||
alertDialog.setCancelable(true);
|
||||
|
||||
alertDialog.setMessage(Html.fromHtml(html));
|
||||
|
||||
alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "I Acknowledge", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
acceptLiceneseChkBox.setChecked(true);
|
||||
goBtn.setEnabled(true);
|
||||
return;
|
||||
}
|
||||
});
|
||||
alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
acceptLiceneseChkBox.setChecked(false);
|
||||
goBtn.setEnabled(false);
|
||||
}
|
||||
});
|
||||
alertDialog.show();
|
||||
}
|
||||
|
||||
public static String filter = null;
|
||||
|
||||
public static class AsyncLogin extends AsyncTask<String, String, String> {
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
|
||||
//this method will be running on UI thread
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String doInBackground(String... params) {
|
||||
HttpsURLConnection con = null;
|
||||
try {
|
||||
URL u = new URL(AppConfig.romsJson);
|
||||
con = (HttpsURLConnection) u.openConnection();
|
||||
|
||||
con.connect();
|
||||
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
sb.append(line + "\n");
|
||||
}
|
||||
br.close();
|
||||
Data = sb.toString();
|
||||
|
||||
return (Data);
|
||||
|
||||
} catch (MalformedURLException ex) {
|
||||
ex.printStackTrace();
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
} finally {
|
||||
if (con != null) {
|
||||
try {
|
||||
con.disconnect();
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
return ("unsuccessful!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String result) {
|
||||
|
||||
//this method will be running on UI thread
|
||||
data = new ArrayList<>();
|
||||
|
||||
try {
|
||||
|
||||
JSONArray jArray = new JSONArray(Data);
|
||||
|
||||
// Extract data from json and store into ArrayList as class objects
|
||||
for (int i = 0; i < jArray.length(); i++) {
|
||||
JSONObject json_data = jArray.getJSONObject(i);
|
||||
DataRoms romsData = new DataRoms();
|
||||
romsData.itemName = json_data.getString("rom_name");
|
||||
romsData.itemIcon = json_data.getString("rom_icon");
|
||||
romsData.itemUrl = json_data.getString("rom_url");
|
||||
romsData.itemPath = json_data.getString("rom_path");
|
||||
romsData.itemAvail = json_data.getBoolean("rom_avail");
|
||||
romsData.itemSize = json_data.getString("rom_size");
|
||||
romsData.itemArch = json_data.getString("rom_arch");
|
||||
romsData.itemKernel = json_data.getString("rom_kernel");
|
||||
romsData.itemExtra = json_data.getString("rom_extra");
|
||||
if (filter != null) {
|
||||
if (romsData.itemKernel.toLowerCase().contains(filter.toLowerCase())) {
|
||||
data.add(romsData);
|
||||
}
|
||||
} else {
|
||||
data.add(romsData);
|
||||
}
|
||||
}
|
||||
|
||||
// Setup and Handover data to recyclerview
|
||||
|
||||
} catch (JSONException e) {
|
||||
UIUtils.toastLong(activity, e.toString());
|
||||
}
|
||||
mRVRoms = (RecyclerView) activity.findViewById(R.id.romsRv);
|
||||
mAdapter = new AdapterRoms(activity, data);
|
||||
mRVRoms.setAdapter(mAdapter);
|
||||
mRVRoms.setLayoutManager(new LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
finish();
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
public class RomsJso extends JSONObject {
|
||||
|
||||
public JSONObject makeJSONObject(String imgName, String imgIcon, String imgPath, String imgExtra) {
|
||||
|
||||
JSONObject obj = new JSONObject();
|
||||
|
||||
try {
|
||||
obj.put("imgName", imgName);
|
||||
obj.put("imgIcon", imgIcon);
|
||||
obj.put("imgPath", imgPath);
|
||||
obj.put("imgExtra", imgExtra);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static final String CREDENTIAL_SHARED_PREF = "settings_prefs";
|
||||
|
||||
private void startIconDownload() {
|
||||
new DownloadsImage().execute(selectedIcon);
|
||||
}
|
||||
|
||||
public void onFirstStartup() {
|
||||
if (selected) {
|
||||
if (FileUtils.fileValid(activity, AppConfig.maindirpath + selectedPath)) {
|
||||
SharedPreferences credentials = activity.getSharedPreferences(CREDENTIAL_SHARED_PREF, Context.MODE_PRIVATE);
|
||||
ProgressDialog mProgressDialog = new ProgressDialog(this, R.style.MainDialogTheme);
|
||||
mProgressDialog.setTitle("Data Setup");
|
||||
mProgressDialog.setMessage("Please Wait...");
|
||||
mProgressDialog.setCancelable(false);
|
||||
mProgressDialog.show();
|
||||
//FileInstaller.installFiles(activity, false);
|
||||
SharedPreferences.Editor editor = credentials.edit();
|
||||
editor.putBoolean("isFirstLaunch", Boolean.TRUE);
|
||||
editor.apply();
|
||||
RomsJso obj = new RomsJso();
|
||||
startIconDownload();
|
||||
final File jsonFile = new File(AppConfig.maindirpath + "roms-data" + ".json");
|
||||
|
||||
if (jsonFile.exists()) {
|
||||
try {
|
||||
List<DataMainRoms> data = new ArrayList<>();
|
||||
JSONArray jArray = new JSONArray(FileUtils.readFromFile(MainActivity.activity, jsonFile));
|
||||
|
||||
try {
|
||||
// Extract data from json and store into ArrayList as class objects
|
||||
for (int i = 0; i < jArray.length(); i++) {
|
||||
JSONObject json_data = jArray.getJSONObject(i);
|
||||
DataMainRoms romsMainData = new DataMainRoms();
|
||||
romsMainData.itemName = json_data.getString("imgName");
|
||||
romsMainData.itemIcon = json_data.getString("imgIcon");
|
||||
romsMainData.itemPath = json_data.getString("imgPath");
|
||||
romsMainData.itemExtra = json_data.getString("imgExtra");
|
||||
data.add(romsMainData);
|
||||
}
|
||||
|
||||
} catch (JSONException e) {
|
||||
Toast.makeText(MainActivity.activity, e.toString(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
JSONObject jsonObject = obj.makeJSONObject(selectedName, AppConfig.maindirpath + "icons/" + selectedPath.replace(".IMG", ".jpg"), AppConfig.maindirpath + selectedPath, selectedExtra);
|
||||
jArray.put(jsonObject);
|
||||
try {
|
||||
Writer output = null;
|
||||
output = new BufferedWriter(new FileWriter(jsonFile));
|
||||
output.write(jArray.toString().replace("\\", "").replace("//", "/"));
|
||||
output.close();
|
||||
} catch (Exception e) {
|
||||
UIUtils.toastLong(activity, e.toString());
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
UIUtils.toastLong(activity, e.toString());
|
||||
}
|
||||
} else {
|
||||
JSONObject jsonObject = obj.makeJSONObject(selectedName, AppConfig.maindirpath + "icons/" + selectedPath.replace(".IMG", ".jpg"), AppConfig.maindirpath + selectedPath, selectedExtra);
|
||||
JSONArray jsonArray = new JSONArray();
|
||||
jsonArray.put(jsonObject);
|
||||
try {
|
||||
Writer output = null;
|
||||
output = new BufferedWriter(new FileWriter(jsonFile));
|
||||
output.write(jsonArray.toString().replace("\\", "").replace("//", "/"));
|
||||
output.close();
|
||||
} catch (Exception e) {
|
||||
UIUtils.toastLong(activity, e.toString());
|
||||
}
|
||||
VectrasStatus.logInfo("Welcome to Vectras ♡");
|
||||
}
|
||||
activity.startActivity(new Intent(activity, MainActivity.class));
|
||||
|
||||
/*new Timer().schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
mProgressDialog.dismiss(); }
|
||||
}, 3000);*/
|
||||
} else {
|
||||
AlertDialog ad;
|
||||
ad = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create();
|
||||
ad.setTitle(selectedPath.replace(".IMG", ".vbi") + " Needs to import");
|
||||
ad.setMessage("press import button and select " + selectedPath.replace(".IMG", ".vbi") + " file.");
|
||||
ad.setButton(Dialog.BUTTON_POSITIVE, "IMPORT", (dialog, which) -> {
|
||||
Intent intent = new Intent(ACTION_OPEN_DOCUMENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setType("*/*");
|
||||
|
||||
// Optionally, specify a URI for the file that should appear in the
|
||||
// system file picker when it loads.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.DIRECTORY_DOWNLOADS);
|
||||
}
|
||||
|
||||
startActivityForResult(intent, 0);
|
||||
});
|
||||
ad.setButton(Dialog.BUTTON_NEGATIVE, "DOWNLAOD " + selectedPath.replace(".IMG", ".vbi"), new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
String gt = selectedLink;
|
||||
Intent g = new Intent(Intent.ACTION_VIEW);
|
||||
g.setData(Uri.parse(gt));
|
||||
RomsManagerActivity.activity.startActivity(g);
|
||||
RomsManagerActivity.activity.finish();
|
||||
}
|
||||
});
|
||||
ad.show();
|
||||
}
|
||||
} else {
|
||||
AlertDialog ad;
|
||||
ad = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create();
|
||||
ad.setTitle("Please Select");
|
||||
ad.setMessage("Select the os (operating system) you need.");
|
||||
ad.setButton(Dialog.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
ad.show();
|
||||
}
|
||||
}
|
||||
|
||||
public String getPath(Uri uri) {
|
||||
return FileUtils.getPath(activity, uri);
|
||||
}
|
||||
|
||||
public ProgressDialog progressDialog = null;
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == 0 && resultCode == RESULT_OK) {
|
||||
Uri content_describer = data.getData();
|
||||
File selectedFilePath = new File(getPath(content_describer));
|
||||
if (selectedFilePath.getName().equals(selectedPath.replace(".IMG", ".vbi"))) {
|
||||
|
||||
try {
|
||||
progressDialog = new ProgressDialog(activity,
|
||||
R.style.MainDialogTheme);
|
||||
progressDialog.setTitle("Extracting");
|
||||
progressDialog.setMessage("Please wait...");
|
||||
progressDialog.setCancelable(false);
|
||||
progressDialog.show(); // Showing Progress Dialog
|
||||
Thread t = new Thread() {
|
||||
public void run() {
|
||||
FileInputStream zipFile = null;
|
||||
try {
|
||||
zipFile = (FileInputStream) getContentResolver().openInputStream(content_describer);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
File targetDirectory = new File(AppConfig.maindirpath);
|
||||
ZipInputStream zis = null;
|
||||
zis = new ZipInputStream(
|
||||
new BufferedInputStream(zipFile));
|
||||
try {
|
||||
ZipEntry ze;
|
||||
int count;
|
||||
byte[] buffer = new byte[8192];
|
||||
while ((ze = zis.getNextEntry()) != null) {
|
||||
File file = new File(targetDirectory, ze.getName());
|
||||
File dir = ze.isDirectory() ? file : file.getParentFile();
|
||||
if (!dir.isDirectory() && !dir.mkdirs())
|
||||
throw new FileNotFoundException("Failed to ensure directory: " +
|
||||
dir.getAbsolutePath());
|
||||
if (ze.isDirectory())
|
||||
continue;
|
||||
try (FileOutputStream fout = new FileOutputStream(file)) {
|
||||
while ((count = zis.read(buffer)) != -1)
|
||||
fout.write(buffer, 0, count);
|
||||
}
|
||||
/* if time should be restored as well
|
||||
long time = ze.getTime();
|
||||
if (time > 0)
|
||||
file.setLastModified(time);
|
||||
*/
|
||||
}
|
||||
} catch (IOException e) {
|
||||
UIUtils.toastLong(activity, e.toString());
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
progressDialog.cancel(); // cancelling Dialog.
|
||||
try {
|
||||
zis.close();
|
||||
} catch (IOException e) {
|
||||
UIUtils.toastLong(activity, e.toString());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
t.start();
|
||||
} catch (Exception e) {
|
||||
progressDialog.dismiss(); // Close Progress Dialog
|
||||
UIUtils.toastLong(activity, e.toString());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
} else {
|
||||
MainActivity.UIAlert("File not supported", "please select " + selectedPath.replace(".IMG", ".vbi") + " file to continue.", activity);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static final int DIALOG_DOWNLOAD_PROGRESS = 0;
|
||||
|
||||
static class DownloadsImage extends AsyncTask<String, Void, Void> {
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(String... strings) {
|
||||
URL url = null;
|
||||
try {
|
||||
url = new URL(strings[0]);
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Bitmap bm = null;
|
||||
try {
|
||||
bm = BitmapFactory.decodeStream(url.openConnection().getInputStream());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
//Create Path to save Image
|
||||
File path = new File(AppConfig.maindirpath + "icons"); //Creates app specific folder
|
||||
|
||||
if (!path.exists()) {
|
||||
path.mkdirs();
|
||||
}
|
||||
|
||||
File imageFile = new File(path, selectedPath.replace(".IMG", ".jpg")); // Imagename.png
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
out = new FileOutputStream(imageFile);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
try {
|
||||
bm.compress(Bitmap.CompressFormat.PNG, 100, out); // Compress Image
|
||||
out.flush();
|
||||
out.close();
|
||||
// Tell the media scanner about the new file so that it is
|
||||
// immediately available to the user.
|
||||
MediaScannerConnection.scanFile(activity, new String[]{imageFile.getAbsolutePath()}, null, new MediaScannerConnection.OnScanCompletedListener() {
|
||||
public void onScanCompleted(String path, Uri uri) {
|
||||
// Log.i("ExternalStorage", "Scanned " + path + ":");
|
||||
// Log.i("ExternalStorage", "-> uri=" + uri);
|
||||
}
|
||||
});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid) {
|
||||
super.onPostExecute(aVoid);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
super.onBackPressed();
|
||||
if (getParentActivityIntent() == MainActivity.activity.getIntent())
|
||||
finish();
|
||||
}
|
||||
|
||||
}
|
||||
85
app/src/main/java/com/vectras/vm/SplashActivity.java
Normal file
85
app/src/main/java/com/vectras/vm/SplashActivity.java
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
package com.vectras.vm;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
|
||||
import android.Manifest;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Dialog;
|
||||
import android.content.*;
|
||||
import android.content.pm.*;
|
||||
import android.net.Uri;
|
||||
import android.os.*;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.provider.Settings;
|
||||
import android.view.*;
|
||||
import android.graphics.*;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.airbnb.lottie.LottieAnimationView;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.vectras.qemu.MainActivityCommon;
|
||||
import com.vectras.qemu.MainSettingsManager;
|
||||
import com.vectras.qemu.utils.RamInfo;
|
||||
import com.vectras.vm.R;
|
||||
import com.vectras.vm.MainActivity;
|
||||
import com.vectras.vm.ui.login.LoginActivity;
|
||||
import com.vectras.vm.utils.UIUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class SplashActivity extends AppCompatActivity implements Runnable {
|
||||
public AlertDialog ad;
|
||||
public static SplashActivity activity;
|
||||
private FirebaseAuth mAuth;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
activity = this;
|
||||
File baseDir = new File(AppConfig.basefiledir);
|
||||
if (!baseDir.exists()) {
|
||||
baseDir.mkdirs();
|
||||
}
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
setContentView(R.layout.activity_splash);
|
||||
|
||||
new Handler().postDelayed(this, 3000);
|
||||
File sharedDir = new File(AppConfig.sharedFolder);
|
||||
if (!sharedDir.exists()) {
|
||||
sharedDir.mkdirs();
|
||||
}
|
||||
File mainDir = new File(AppConfig.maindirpath);
|
||||
if (!mainDir.exists()) {
|
||||
mainDir.mkdirs();
|
||||
}
|
||||
// Initialize Firebase Auth
|
||||
mAuth = FirebaseAuth.getInstance();
|
||||
RamInfo.activity = activity;
|
||||
|
||||
MainSettingsManager.setOrientationSetting(activity, 1);
|
||||
}
|
||||
|
||||
public static final String CREDENTIAL_SHARED_PREF = "settings_prefs";
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
SharedPreferences prefs = getSharedPreferences(CREDENTIAL_SHARED_PREF, Context.MODE_PRIVATE);
|
||||
|
||||
boolean isAccessed = prefs.getBoolean("isFirstLaunch", false);
|
||||
if (!isAccessed) {
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
} else {
|
||||
if (FirebaseAuth.getInstance().getCurrentUser() != null)
|
||||
startActivity(new Intent(this, MainActivity.class));
|
||||
else
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
}
|
||||
finish();
|
||||
}
|
||||
}
|
||||
103
app/src/main/java/com/vectras/vm/Store/AdapterStore.java
Normal file
103
app/src/main/java/com/vectras/vm/Store/AdapterStore.java
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
package com.vectras.vm.Store;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.vectras.vm.R;
|
||||
import com.vectras.vm.Fragment.HomeFragment;
|
||||
import com.vectras.vm.PostActivity;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import com.vectras.vm.MainActivity;
|
||||
import com.vectras.vm.StoreActivity;
|
||||
import com.vectras.vm.StoreItemActivity;
|
||||
|
||||
public class AdapterStore extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
|
||||
private Context context;
|
||||
private LayoutInflater inflater;
|
||||
List<DataStore> data = Collections.emptyList();
|
||||
DataStore current;
|
||||
int currentPos = 0;
|
||||
|
||||
// create constructor to innitilize context and data sent from MainActivity
|
||||
public AdapterStore(Context context, List<DataStore> data) {
|
||||
this.context = context;
|
||||
inflater = LayoutInflater.from(context);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
// Inflate the layout when viewholder created
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = inflater.inflate(R.layout.container_store, parent, false);
|
||||
MyHolder holder = new MyHolder(view);
|
||||
return holder;
|
||||
}
|
||||
|
||||
// Bind data
|
||||
@Override
|
||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
||||
|
||||
// Get current position of item in recyclerview to bind data and assign values from list
|
||||
MyHolder myHolder = (MyHolder) holder;
|
||||
final DataStore current = data.get(position);
|
||||
myHolder.textName.setText(current.itemName);
|
||||
myHolder.textSize.setText("Size: " + current.itemSize);
|
||||
Glide.with(StoreActivity.activity).load(current.itemIcon).into(myHolder.ivIcon);
|
||||
Animation animation;
|
||||
animation = AnimationUtils.loadAnimation(MainActivity.activity, android.R.anim.slide_in_left);
|
||||
animation.setDuration(300);
|
||||
|
||||
myHolder.cdItem.startAnimation(animation);
|
||||
animation = null;
|
||||
myHolder.cdItem.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View view) {
|
||||
StoreItemActivity.name = current.itemName;
|
||||
StoreItemActivity.icon = current.itemIcon;
|
||||
StoreItemActivity.size = current.itemSize;
|
||||
StoreItemActivity.desc = current.itemData;
|
||||
StoreItemActivity.link = current.itemLink;
|
||||
StoreItemActivity.prvMain = current.itemPreviewMain;
|
||||
StoreItemActivity.prv1 = current.itemPreview1;
|
||||
StoreItemActivity.prv2 = current.itemPreview2;
|
||||
StoreActivity.activity.startActivity(new Intent(StoreActivity.activity, StoreItemActivity.class));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// return total item from List
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
class MyHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
CardView cdItem;
|
||||
TextView textName;
|
||||
ImageView ivIcon;
|
||||
TextView textSize;
|
||||
|
||||
// create constructor to get widget reference
|
||||
public MyHolder(View itemView) {
|
||||
super(itemView);
|
||||
cdItem = (CardView) itemView.findViewById(R.id.cdItem);
|
||||
textName = (TextView) itemView.findViewById(R.id.textName);
|
||||
ivIcon = (ImageView) itemView.findViewById(R.id.ivIcon);
|
||||
textSize = (TextView) itemView.findViewById(R.id.textSize);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
13
app/src/main/java/com/vectras/vm/Store/DataStore.java
Normal file
13
app/src/main/java/com/vectras/vm/Store/DataStore.java
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package com.vectras.vm.Store;
|
||||
|
||||
public class DataStore {
|
||||
|
||||
public String itemName;
|
||||
public String itemSize;
|
||||
public String itemData;
|
||||
public String itemIcon;
|
||||
public String itemLink;
|
||||
public String itemPreviewMain;
|
||||
public String itemPreview1;
|
||||
public String itemPreview2;
|
||||
}
|
||||
204
app/src/main/java/com/vectras/vm/StoreActivity.java
Normal file
204
app/src/main/java/com/vectras/vm/StoreActivity.java
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
package com.vectras.vm;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.*;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.google.android.gms.ads.AdRequest;
|
||||
import com.google.android.gms.ads.AdView;
|
||||
import com.vectras.vm.R;
|
||||
import com.vectras.vm.Blog.AdapterBlog;
|
||||
import com.vectras.vm.Blog.DataBlog;
|
||||
import com.vectras.vm.Fragment.HomeFragment;
|
||||
import com.vectras.vm.Store.AdapterStore;
|
||||
import com.vectras.vm.Store.DataStore;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
public class StoreActivity extends AppCompatActivity{
|
||||
private RecyclerView mRVStore;
|
||||
private AdapterStore mAdapter;
|
||||
public static LinearLayout noConnectionLayout;
|
||||
public SwipeRefreshLayout pullToRefresh;
|
||||
public static StoreActivity activity;
|
||||
public String Data;
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
setContentView(R.layout.activity_store);
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
toolbar.setTitle(getString(R.string.app_name));
|
||||
|
||||
activity = this;
|
||||
|
||||
AdView mAdView = findViewById(R.id.adView);
|
||||
AdRequest adRequest = new AdRequest.Builder().build();
|
||||
mAdView.loadAd(adRequest);
|
||||
noConnectionLayout = findViewById(R.id.noConnectionLayout);
|
||||
mRVStore = findViewById(R.id.storeRv);
|
||||
|
||||
if (checkConnection(activity)) {
|
||||
new StoreActivity.AsyncLogin().execute();
|
||||
noConnectionLayout.setVisibility(View.GONE);
|
||||
//mRVBlog.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
noConnectionLayout.setVisibility(View.VISIBLE);
|
||||
//mRVBlog.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
pullToRefresh = findViewById(R.id.refreshLayout);
|
||||
pullToRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
if (checkConnection(activity)) {
|
||||
new StoreActivity.AsyncLogin().execute();
|
||||
} else {
|
||||
noConnectionLayout.setVisibility(View.VISIBLE);
|
||||
pullToRefresh.setRefreshing(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
public boolean checkConnection(Context context) {
|
||||
final ConnectivityManager connMgr = (ConnectivityManager) context
|
||||
.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
|
||||
if (connMgr != null) {
|
||||
NetworkInfo activeNetworkInfo = connMgr.getActiveNetworkInfo();
|
||||
|
||||
if (activeNetworkInfo != null) { // connected to the internet
|
||||
// connected to the mobile provider's data plan
|
||||
if (activeNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
|
||||
// connected to wifi
|
||||
return true;
|
||||
} else
|
||||
return activeNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private class AsyncLogin extends AsyncTask<String, String, String> {
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
|
||||
//this method will be running on UI thread
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String doInBackground(String... params) {
|
||||
HttpsURLConnection con = null;
|
||||
try {
|
||||
URL u = new URL(AppConfig.storeJson);
|
||||
con = (HttpsURLConnection) u.openConnection();
|
||||
|
||||
con.connect();
|
||||
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
sb.append(line + "\n");
|
||||
}
|
||||
br.close();
|
||||
Data = sb.toString();
|
||||
|
||||
return (Data);
|
||||
|
||||
} catch (MalformedURLException ex) {
|
||||
ex.printStackTrace();
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
} finally {
|
||||
if (con != null) {
|
||||
try {
|
||||
con.disconnect();
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
return ("unsuccessful!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String result) {
|
||||
|
||||
//this method will be running on UI thread
|
||||
pullToRefresh.setRefreshing(false);
|
||||
|
||||
noConnectionLayout.setVisibility(View.GONE);
|
||||
|
||||
List<DataStore> data = new ArrayList<>();
|
||||
|
||||
try {
|
||||
|
||||
JSONArray jArray = new JSONArray(Data);
|
||||
|
||||
// Extract data from json and store into ArrayList as class objects
|
||||
for (int i = 0; i < jArray.length(); i++) {
|
||||
JSONObject json_data = jArray.getJSONObject(i);
|
||||
DataStore storeData = new DataStore();
|
||||
storeData.itemName = json_data.getString("item_name");
|
||||
storeData.itemIcon = json_data.getString("item_icon");
|
||||
storeData.itemData = json_data.getString("item_data");
|
||||
storeData.itemSize = json_data.getString("item_size");
|
||||
storeData.itemLink = json_data.getString("item_link");
|
||||
storeData.itemPreviewMain = json_data.getString("item_preview_main");
|
||||
storeData.itemPreview1 = json_data.getString("item_preview_1");
|
||||
storeData.itemPreview2 = json_data.getString("item_preview_2");
|
||||
data.add(storeData);
|
||||
}
|
||||
|
||||
// Setup and Handover data to recyclerview
|
||||
|
||||
} catch (JSONException e) {
|
||||
Toast.makeText(activity, e.toString(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
mRVStore = (RecyclerView) findViewById(R.id.storeRv);
|
||||
mAdapter = new AdapterStore(activity, data);
|
||||
mRVStore.setAdapter(mAdapter);
|
||||
mRVStore.setLayoutManager(new LinearLayoutManager(activity));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if(item.getItemId()== android.R.id.home){
|
||||
finish();
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
304
app/src/main/java/com/vectras/vm/StoreItemActivity.java
Normal file
304
app/src/main/java/com/vectras/vm/StoreItemActivity.java
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
package com.vectras.vm;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.IntentService;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.*;
|
||||
import android.text.Html;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.webkit.URLUtil;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.os.PowerManager;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.google.android.gms.ads.AdRequest;
|
||||
import com.google.android.gms.ads.AdView;
|
||||
import com.google.android.gms.ads.LoadAdError;
|
||||
import com.google.android.gms.ads.MobileAds;
|
||||
import com.google.android.gms.ads.initialization.InitializationStatus;
|
||||
import com.google.android.gms.ads.initialization.OnInitializationCompleteListener;
|
||||
import com.google.android.gms.ads.interstitial.InterstitialAd;
|
||||
import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback;
|
||||
import com.vectras.vm.utils.FileUtils;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import com.vectras.vm.R;
|
||||
import com.vectras.vm.utils.UIUtils;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import android.os.PowerManager;
|
||||
|
||||
public class StoreItemActivity extends AppCompatActivity {
|
||||
public StoreItemActivity activity;
|
||||
public String TAG = "StoreItemActivity";
|
||||
public static String icon, name, size, desc, descStr, prvMain, prv1, prv2, link;
|
||||
public TextView itemName, itemSize, itemDesc;
|
||||
public Button dBtn;
|
||||
public ImageView itemIcon, itemPrvMain, itemPrv1, itemPrv2;
|
||||
|
||||
private InterstitialAd mInterstitialAd;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
activity = this;
|
||||
super.onCreate(bundle);
|
||||
setContentView(R.layout.activity_store_item);
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
toolbar.setTitle(getString(R.string.app_name));
|
||||
itemIcon = findViewById(R.id.ivIcon);
|
||||
itemName = findViewById(R.id.textName);
|
||||
itemSize = findViewById(R.id.textSize);
|
||||
dBtn = findViewById(R.id.btn_download);
|
||||
itemDesc = findViewById(R.id.descTxt);
|
||||
itemPrvMain = findViewById(R.id.ivPrvMain);
|
||||
itemPrv1 = findViewById(R.id.ivPrv1);
|
||||
itemPrv2 = findViewById(R.id.ivPrv2);
|
||||
|
||||
AdView mAdView = findViewById(R.id.adView);
|
||||
AdRequest adRequest = new AdRequest.Builder().build();
|
||||
mAdView.loadAd(adRequest);
|
||||
|
||||
MobileAds.initialize(this, new OnInitializationCompleteListener() {
|
||||
@Override
|
||||
public void onInitializationComplete(InitializationStatus initializationStatus) {}
|
||||
});
|
||||
InterstitialAd.load(this,"ca-app-pub-3568137780412047/4892595373", adRequest,
|
||||
new InterstitialAdLoadCallback() {
|
||||
@Override
|
||||
public void onAdLoaded(@NonNull InterstitialAd interstitialAd) {
|
||||
// The mInterstitialAd reference will be null until
|
||||
// an ad is loaded.
|
||||
mInterstitialAd = interstitialAd;
|
||||
Log.i(TAG, "onAdLoaded");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {
|
||||
// Handle the error
|
||||
Log.d(TAG, loadAdError.toString());
|
||||
mInterstitialAd = null;
|
||||
}
|
||||
});
|
||||
if (mInterstitialAd != null) {
|
||||
mInterstitialAd.show(StoreItemActivity.this);
|
||||
} else {
|
||||
Log.d("TAG", "The interstitial ad wasn't ready yet.");
|
||||
}
|
||||
dBtn.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
|
||||
InterstitialAd.load(activity,"ca-app-pub-3568137780412047/7937545204", adRequest,
|
||||
new InterstitialAdLoadCallback() {
|
||||
@Override
|
||||
public void onAdLoaded(@NonNull InterstitialAd interstitialAd) {
|
||||
// The mInterstitialAd reference will be null until
|
||||
// an ad is loaded.
|
||||
mInterstitialAd = interstitialAd;
|
||||
Log.i(TAG, "onAdLoaded");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {
|
||||
// Handle the error
|
||||
Log.d(TAG, loadAdError.toString());
|
||||
mInterstitialAd = null;
|
||||
}
|
||||
});
|
||||
startDownload();
|
||||
}
|
||||
});
|
||||
itemName.setText(name);
|
||||
itemSize.setText(size);
|
||||
|
||||
Glide.with(this).load(icon).into(itemIcon);
|
||||
Glide.with(this).load(prvMain).into(itemPrvMain);
|
||||
Glide.with(this).load(prv1).into(itemPrv1);
|
||||
Glide.with(this).load(prv2).into(itemPrv2);
|
||||
new Thread(new Runnable() {
|
||||
|
||||
public void run() {
|
||||
|
||||
BufferedReader reader = null;
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
||||
try {
|
||||
// Create a URL for the desired page
|
||||
URL url = new URL(desc); //My text file location
|
||||
//First open the connection
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setConnectTimeout(60000); // timing out in a minute
|
||||
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
|
||||
//t=(TextView)findViewById(R.id.TextView1); // ideally do this in onCreate()
|
||||
String str;
|
||||
while ((str = in.readLine()) != null) {
|
||||
builder.append(str);
|
||||
}
|
||||
in.close();
|
||||
} catch (Exception e) {
|
||||
itemDesc.setText("no internet connection");
|
||||
UIUtils.toastLong(StoreItemActivity.this, "check your internet connection");
|
||||
Log.d("VECTRAS", e.toString());
|
||||
}
|
||||
|
||||
//since we are in background thread, to post results we have to go back to ui thread. do the following for that
|
||||
|
||||
StoreItemActivity.this.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
descStr = builder.toString(); // My TextFile has 3 lines
|
||||
itemDesc.setText(Html.fromHtml(descStr));
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}).start();
|
||||
itemPrvMain.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ImagePrvActivity.linkIv = prvMain;
|
||||
startActivity(new Intent(activity, ImagePrvActivity.class));
|
||||
}
|
||||
});
|
||||
itemPrv1.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ImagePrvActivity.linkIv = prv1;
|
||||
startActivity(new Intent(activity, ImagePrvActivity.class));
|
||||
}
|
||||
});
|
||||
itemPrv2.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ImagePrvActivity.linkIv = prv2;
|
||||
startActivity(new Intent(activity, ImagePrvActivity.class));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static final int DIALOG_DOWNLOAD_PROGRESS = 0;
|
||||
private ProgressDialog mProgressDialog;
|
||||
|
||||
private void startDownload() {
|
||||
String url = link;
|
||||
new DownloadFileAsync().execute(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
switch (id) {
|
||||
case DIALOG_DOWNLOAD_PROGRESS:
|
||||
mProgressDialog = new ProgressDialog(this, R.style.MainDialogTheme);
|
||||
mProgressDialog.setMessage("Downloading file..");
|
||||
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||
mProgressDialog.setCancelable(false);
|
||||
mProgressDialog.show();
|
||||
return mProgressDialog;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class DownloadFileAsync extends AsyncTask<String, String, String> {
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
showDialog(DIALOG_DOWNLOAD_PROGRESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String doInBackground(String... aurl) {
|
||||
int count;
|
||||
|
||||
try {
|
||||
URL url = new URL(aurl[0]);
|
||||
URLConnection conexion = url.openConnection();
|
||||
conexion.connect();
|
||||
|
||||
int lenghtOfFile = conexion.getContentLength();
|
||||
Log.d(TAG, "Lenght of file: " + lenghtOfFile);
|
||||
String fileName = URLUtil.guessFileName(link,null,null);
|
||||
InputStream input = new BufferedInputStream(url.openStream());
|
||||
OutputStream output = new FileOutputStream(AppConfig.sharedFolder+fileName);
|
||||
|
||||
byte data[] = new byte[1024];
|
||||
|
||||
long total = 0;
|
||||
|
||||
while ((count = input.read(data)) != -1) {
|
||||
total += count;
|
||||
publishProgress("" + (int) ((total * 100) / lenghtOfFile));
|
||||
output.write(data, 0, count);
|
||||
}
|
||||
|
||||
output.flush();
|
||||
output.close();
|
||||
input.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
protected void onProgressUpdate(String... progress) {
|
||||
Log.d(TAG, progress[0]);
|
||||
mProgressDialog.setProgress(Integer.parseInt(progress[0]));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String unused) {
|
||||
dismissDialog(DIALOG_DOWNLOAD_PROGRESS);
|
||||
AlertDialog ad;
|
||||
ad = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create();
|
||||
ad.setTitle("Downloaded Successfully!");
|
||||
String fileName = URLUtil.guessFileName(link,null,null);
|
||||
ad.setMessage("Downloaded to path: "+AppConfig.sharedFolder+fileName+" boot vectras to check your downloads in QEMU VFAT partition.");
|
||||
ad.setButton(Dialog.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
ad.show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
finish();
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
379
app/src/main/java/com/vectras/vm/VectrasApp.java
Normal file
379
app/src/main/java/com/vectras/vm/VectrasApp.java
Normal file
|
|
@ -0,0 +1,379 @@
|
|||
package com.vectras.vm;
|
||||
|
||||
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.HorizontalScrollView;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
|
||||
import com.google.android.material.color.DynamicColors;
|
||||
import com.vectras.qemu.MainSettingsManager;
|
||||
import com.vectras.vm.logger.VectrasStatus;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class VectrasApp extends Application {
|
||||
|
||||
private static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
|
||||
private static Context app;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
CrashHandler.getInstance().registerGlobal(this);
|
||||
CrashHandler.getInstance().registerPart(this);
|
||||
try {
|
||||
Class.forName("android.os.AsyncTask");
|
||||
} catch (Throwable ignore) {
|
||||
// ignored
|
||||
}
|
||||
app = getApplicationContext();
|
||||
setModeNight(this);
|
||||
DynamicColors.applyToActivitiesIfAvailable(this);
|
||||
}
|
||||
|
||||
private void setModeNight(Context context) {
|
||||
if (MainSettingsManager.getModeNight(context)) {
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
|
||||
setTheme(R.style.AppTheme);
|
||||
} else {
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
||||
setTheme(R.style.AppTheme);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static Context getApp() {
|
||||
return app;
|
||||
}
|
||||
|
||||
public static void write(InputStream input, OutputStream output) throws IOException {
|
||||
byte[] buf = new byte[1024 * 8];
|
||||
int len;
|
||||
while ((len = input.read(buf)) != -1) {
|
||||
output.write(buf, 0, len);
|
||||
}
|
||||
}
|
||||
|
||||
public static void write(File file, byte[] data) throws IOException {
|
||||
File parent = file.getParentFile();
|
||||
if (parent != null && !parent.exists())
|
||||
parent.mkdirs();
|
||||
|
||||
ByteArrayInputStream input = new ByteArrayInputStream(data);
|
||||
FileOutputStream output = new FileOutputStream(file);
|
||||
try {
|
||||
write(input, output);
|
||||
} finally {
|
||||
closeIO(input, output);
|
||||
}
|
||||
}
|
||||
|
||||
public static String toString(InputStream input) throws IOException {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
write(input, output);
|
||||
try {
|
||||
return output.toString("UTF-8");
|
||||
} finally {
|
||||
closeIO(input, output);
|
||||
}
|
||||
}
|
||||
|
||||
public static void closeIO(Closeable... closeables) {
|
||||
for (Closeable closeable : closeables) {
|
||||
try {
|
||||
if (closeable != null)
|
||||
closeable.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class CrashHandler {
|
||||
|
||||
public static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER = Thread
|
||||
.getDefaultUncaughtExceptionHandler();
|
||||
|
||||
private static CrashHandler sInstance;
|
||||
|
||||
private PartCrashHandler mPartCrashHandler;
|
||||
|
||||
public static CrashHandler getInstance() {
|
||||
if (sInstance == null) {
|
||||
sInstance = new CrashHandler();
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
public void registerGlobal(Context context) {
|
||||
registerGlobal(context, null);
|
||||
}
|
||||
|
||||
public void registerGlobal(Context context, String crashDir) {
|
||||
Thread.setDefaultUncaughtExceptionHandler(
|
||||
new UncaughtExceptionHandlerImpl(context.getApplicationContext(), crashDir));
|
||||
}
|
||||
|
||||
public void unregister() {
|
||||
Thread.setDefaultUncaughtExceptionHandler(DEFAULT_UNCAUGHT_EXCEPTION_HANDLER);
|
||||
}
|
||||
|
||||
public void registerPart(Context context) {
|
||||
unregisterPart(context);
|
||||
mPartCrashHandler = new PartCrashHandler(context.getApplicationContext());
|
||||
MAIN_HANDLER.postAtFrontOfQueue(mPartCrashHandler);
|
||||
}
|
||||
|
||||
public void unregisterPart(Context context) {
|
||||
if (mPartCrashHandler != null) {
|
||||
mPartCrashHandler.isRunning.set(false);
|
||||
mPartCrashHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class PartCrashHandler implements Runnable {
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
public AtomicBoolean isRunning = new AtomicBoolean(true);
|
||||
|
||||
public PartCrashHandler(Context context) {
|
||||
this.mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (isRunning.get()) {
|
||||
try {
|
||||
Looper.loop();
|
||||
} catch (final Throwable e) {
|
||||
e.printStackTrace();
|
||||
if (isRunning.get()) {
|
||||
MAIN_HANDLER.post(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
VectrasStatus.logError("<font color='red'>[E] "+e.getMessage()+"</font>");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (e instanceof RuntimeException) {
|
||||
throw (RuntimeException) e;
|
||||
} else {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class UncaughtExceptionHandlerImpl implements UncaughtExceptionHandler {
|
||||
|
||||
private static DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy_MM_dd-HH_mm_ss");
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
private final File mCrashDir;
|
||||
|
||||
public UncaughtExceptionHandlerImpl(Context context, String crashDir) {
|
||||
this.mContext = context;
|
||||
this.mCrashDir = TextUtils.isEmpty(crashDir) ? new File(mContext.getExternalCacheDir(), "crash")
|
||||
: new File(crashDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable throwable) {
|
||||
try {
|
||||
|
||||
String log = buildLog(throwable);
|
||||
writeLog(log);
|
||||
|
||||
try {
|
||||
Intent intent = new Intent(mContext, CrashActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra(Intent.EXTRA_TEXT, log);
|
||||
mContext.startActivity(intent);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
writeLog(e.toString());
|
||||
}
|
||||
|
||||
throwable.printStackTrace();
|
||||
android.os.Process.killProcess(android.os.Process.myPid());
|
||||
System.exit(0);
|
||||
|
||||
} catch (Throwable e) {
|
||||
if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null)
|
||||
DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(thread, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildLog(Throwable throwable) {
|
||||
String time = DATE_FORMAT.format(new Date());
|
||||
|
||||
String versionName = "unknown";
|
||||
long versionCode = 0;
|
||||
try {
|
||||
PackageInfo packageInfo = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0);
|
||||
versionName = packageInfo.versionName;
|
||||
versionCode = Build.VERSION.SDK_INT >= 28 ? packageInfo.getLongVersionCode()
|
||||
: packageInfo.versionCode;
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
LinkedHashMap<String, String> head = new LinkedHashMap<String, String>();
|
||||
head.put("Time Of Crash", time);
|
||||
head.put("Device", String.format("%s, %s", Build.MANUFACTURER, Build.MODEL));
|
||||
head.put("Android Version", String.format("%s (%d)", Build.VERSION.RELEASE, Build.VERSION.SDK_INT));
|
||||
head.put("App Version", String.format("%s (%d)", versionName, versionCode));
|
||||
head.put("Kernel", getKernel());
|
||||
head.put("Support Abis",
|
||||
Build.VERSION.SDK_INT >= 21 && Build.SUPPORTED_ABIS != null
|
||||
? Arrays.toString(Build.SUPPORTED_ABIS)
|
||||
: "unknown");
|
||||
head.put("Fingerprint", Build.FINGERPRINT);
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
for (String key : head.keySet()) {
|
||||
if (builder.length() != 0)
|
||||
builder.append("\n");
|
||||
builder.append(key);
|
||||
builder.append(" : ");
|
||||
builder.append(head.get(key));
|
||||
}
|
||||
|
||||
builder.append("\n\n");
|
||||
builder.append(Log.getStackTraceString(throwable));
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private void writeLog(String log) {
|
||||
String time = DATE_FORMAT.format(new Date());
|
||||
File file = new File(mCrashDir, "crash_" + time + ".txt");
|
||||
try {
|
||||
write(file, log.getBytes("UTF-8"));
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static String getKernel() {
|
||||
try {
|
||||
return VectrasApp.toString(new FileInputStream("/proc/version")).trim();
|
||||
} catch (Throwable e) {
|
||||
return e.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final class CrashActivity extends Activity {
|
||||
|
||||
private String mLog;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setTheme(android.R.style.Theme_DeviceDefault);
|
||||
setTitle("App Crash");
|
||||
|
||||
mLog = getIntent().getStringExtra(Intent.EXTRA_TEXT);
|
||||
|
||||
ScrollView contentView = new ScrollView(this);
|
||||
contentView.setFillViewport(true);
|
||||
|
||||
HorizontalScrollView horizontalScrollView = new HorizontalScrollView(this);
|
||||
|
||||
TextView textView = new TextView(this);
|
||||
int padding = dp2px(16);
|
||||
textView.setPadding(padding, padding, padding, padding);
|
||||
textView.setText(mLog);
|
||||
textView.setTextIsSelectable(true);
|
||||
textView.setTypeface(Typeface.DEFAULT);
|
||||
textView.setLinksClickable(true);
|
||||
|
||||
horizontalScrollView.addView(textView);
|
||||
contentView.addView(horizontalScrollView, ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
|
||||
setContentView(contentView);
|
||||
}
|
||||
|
||||
private void restart() {
|
||||
Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName());
|
||||
if (intent != null) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(intent);
|
||||
}
|
||||
finish();
|
||||
android.os.Process.killProcess(android.os.Process.myPid());
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private static int dp2px(float dpValue) {
|
||||
final float scale = Resources.getSystem().getDisplayMetrics().density;
|
||||
return (int) (dpValue * scale + 0.5f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
menu.add(0, android.R.id.copy, 0, android.R.string.copy).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.copy:
|
||||
ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
cm.setPrimaryClip(ClipData.newPlainText(getPackageName(), mLog));
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
54
app/src/main/java/com/vectras/vm/WidgetProvider.java
Normal file
54
app/src/main/java/com/vectras/vm/WidgetProvider.java
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package com.vectras.vm;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProvider;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.widget.ListView;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.vectras.vm.MainRoms.AdapterMainRoms;
|
||||
import com.vectras.vm.MainRoms.DataMainRoms;
|
||||
import com.vectras.vm.utils.FileUtils;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class WidgetProvider extends AppWidgetProvider {
|
||||
|
||||
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
||||
// Perform this loop procedure for each widget that belongs to this
|
||||
// provider.
|
||||
for (int i=0; i < appWidgetIds.length; i++) {
|
||||
int appWidgetId = appWidgetIds[i];
|
||||
// Create an Intent to launch ExampleActivity
|
||||
Intent intent = new Intent(context, MainActivity.class);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||
/* context = */ context,
|
||||
/* requestCode = */ 0,
|
||||
/* intent = */ intent,
|
||||
/* flags = */ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
|
||||
);
|
||||
|
||||
// Get the layout for the widget and attach an onClick listener to
|
||||
// the button.
|
||||
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widgetlayout);
|
||||
views.setOnClickPendingIntent(R.id.button, pendingIntent);
|
||||
views.setRemoteAdapter(R.id.mRVMainRoms, intent);
|
||||
// Tell the AppWidgetManager to perform an update on the current app
|
||||
// widget.
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views);
|
||||
}
|
||||
}
|
||||
}
|
||||
385
app/src/main/java/com/vectras/vm/adapter/LogsAdapter.java
Normal file
385
app/src/main/java/com/vectras/vm/adapter/LogsAdapter.java
Normal file
|
|
@ -0,0 +1,385 @@
|
|||
package com.vectras.vm.adapter;
|
||||
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
|
||||
import com.vectras.vm.logger.LogItem;
|
||||
import java.util.Collections;
|
||||
import android.widget.TextView;
|
||||
import android.content.Context;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.LayoutInflater;
|
||||
import java.util.Vector;
|
||||
import android.database.DataSetObserver;
|
||||
import java.util.Date;
|
||||
import android.text.format.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Locale;
|
||||
import android.os.Message;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.View;
|
||||
import com.vectras.vm.R;
|
||||
import android.text.Html;
|
||||
import android.view.MotionEvent;
|
||||
import com.vectras.vm.logger.VectrasStatus;
|
||||
|
||||
public class LogsAdapter extends RecyclerView.Adapter<LogsAdapter.logViewHolder>
|
||||
implements VectrasStatus.LogListener ,Handler.Callback,
|
||||
View.OnTouchListener
|
||||
{
|
||||
private static final int MESSAGE_NEWLOG = 0;
|
||||
|
||||
private static final int MESSAGE_CLEARLOG = 1;
|
||||
|
||||
private static final int MESSAGE_NEWTS = 2;
|
||||
private static final int MESSAGE_NEWLOGLEVEL = 3;
|
||||
|
||||
public static final int TIME_FORMAT_NONE = 0;
|
||||
public static final int TIME_FORMAT_SHORT = 1;
|
||||
public static final int TIME_FORMAT_ISO = 2;
|
||||
private static final int MAX_STORED_LOG_ENTRIES = 1000;
|
||||
|
||||
private Vector<LogItem> allEntries = new Vector<>();
|
||||
|
||||
private Vector<LogItem> currentLevelEntries = new Vector<LogItem>();
|
||||
|
||||
private Handler mHandler;
|
||||
private Context mContext;
|
||||
private OnItemClickListener itemClickListener;
|
||||
private LinearLayoutManager mLinearLayoutManager;
|
||||
|
||||
private Vector<AdapterDataObserver> observers = new Vector<>();
|
||||
|
||||
private int mTimeFormat = -100;
|
||||
private int mLogLevel = 3;
|
||||
private boolean mLockAutoScroll = false;
|
||||
|
||||
|
||||
/**
|
||||
* Interfaces
|
||||
*/
|
||||
|
||||
public interface OnItemClickListener
|
||||
{
|
||||
void onItemClick(View view, int position, String logText);
|
||||
void onItemLongClick(View view, int position, String logText);
|
||||
}
|
||||
|
||||
public class logViewHolder extends RecyclerView.ViewHolder
|
||||
{
|
||||
TextView textLog;
|
||||
|
||||
logViewHolder(View itemView)
|
||||
{
|
||||
super(itemView);
|
||||
|
||||
this.textLog = itemView.findViewById(R.id.textLog);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public LogsAdapter(LinearLayoutManager layoutManager,
|
||||
Context context)
|
||||
{
|
||||
this.mContext = context;
|
||||
this.mLinearLayoutManager = layoutManager;
|
||||
|
||||
setLogLevel(VectrasStatus.LogLevel.DEBUG.getInt());
|
||||
|
||||
initLogBuffer();
|
||||
if (mHandler == null)
|
||||
{
|
||||
mHandler = new Handler(this);
|
||||
}
|
||||
|
||||
VectrasStatus.addLogListener(this);
|
||||
}
|
||||
|
||||
public void setOnItemClickListener(OnItemClickListener listener) {
|
||||
this.itemClickListener = listener;
|
||||
}
|
||||
|
||||
private void initLogBuffer()
|
||||
{
|
||||
allEntries.clear();
|
||||
Collections.addAll(allEntries, VectrasStatus.getlogbuffer());
|
||||
initCurrentMessages();
|
||||
}
|
||||
|
||||
private void initCurrentMessages()
|
||||
{
|
||||
currentLevelEntries.clear();
|
||||
for (LogItem li : allEntries)
|
||||
{
|
||||
if (li.getLogLevel().getInt() <= mLogLevel)
|
||||
currentLevelEntries.add(li);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public logViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
|
||||
{
|
||||
Context context = parent.getContext();
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
|
||||
View logView = inflater.inflate(R.layout.list_item_log,
|
||||
parent, false);
|
||||
logView.setOnTouchListener(this);
|
||||
|
||||
return new logViewHolder(logView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(final logViewHolder viewHolder,
|
||||
final int position)
|
||||
{
|
||||
final String text;
|
||||
|
||||
try
|
||||
{
|
||||
LogItem logItem = currentLevelEntries.get(position);
|
||||
String msg = logItem.getString(mContext);
|
||||
String time = getTime(logItem, mTimeFormat);
|
||||
text = (!time.isEmpty() ? String.format("[%s] ", time) : "") + msg;
|
||||
viewHolder.textLog.setText(Html.fromHtml(text));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
VectrasStatus.logException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
viewHolder.textLog.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
if (itemClickListener != null)
|
||||
itemClickListener.onItemClick(v, position, text);
|
||||
}
|
||||
});
|
||||
|
||||
viewHolder.textLog.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v)
|
||||
{
|
||||
if (itemClickListener != null)
|
||||
itemClickListener.onItemLongClick(v, position, text);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerAdapterDataObserver(RecyclerView.AdapterDataObserver observer)
|
||||
{
|
||||
super.registerAdapterDataObserver(observer);
|
||||
observers.add(observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver observer)
|
||||
{
|
||||
super.unregisterAdapterDataObserver(observer);
|
||||
observers.remove(observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount()
|
||||
{
|
||||
return currentLevelEntries.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position)
|
||||
{
|
||||
return ((Object) currentLevelEntries.get(position)).hashCode();
|
||||
}
|
||||
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return currentLevelEntries.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToRecyclerView(
|
||||
RecyclerView recyclerView)
|
||||
{
|
||||
super.onAttachedToRecyclerView(recyclerView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View p1, MotionEvent event)
|
||||
{
|
||||
// aqui deveria pausar autoscroll
|
||||
/*int action = event.getAction();
|
||||
|
||||
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_MOVE){
|
||||
mLockAutoScroll = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
mLockAutoScroll = false;*/
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private String getTime(LogItem le, int time)
|
||||
{
|
||||
if (time != TIME_FORMAT_NONE)
|
||||
{
|
||||
Date d = new Date(le.getLogtime());
|
||||
java.text.DateFormat timeformat;
|
||||
if (time == TIME_FORMAT_SHORT)
|
||||
timeformat = new SimpleDateFormat("HH:mm a");
|
||||
else
|
||||
timeformat = DateFormat.getTimeFormat(mContext);
|
||||
|
||||
return timeformat.format(d);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handler implementação
|
||||
*/
|
||||
|
||||
@Override
|
||||
public boolean handleMessage(Message msg)
|
||||
{
|
||||
// We have been called
|
||||
if (msg.what == MESSAGE_NEWLOG)
|
||||
{
|
||||
LogItem logMessage = msg.getData().getParcelable("logmessage");
|
||||
if (addLogMessage(logMessage))
|
||||
{
|
||||
|
||||
for (AdapterDataObserver observer : observers)
|
||||
{
|
||||
observer.onChanged();
|
||||
}
|
||||
|
||||
if (!mLockAutoScroll)
|
||||
scrollToLastPosition();
|
||||
}
|
||||
}
|
||||
else if (msg.what == MESSAGE_CLEARLOG)
|
||||
{
|
||||
for (AdapterDataObserver observer : observers)
|
||||
{
|
||||
observer.onChanged();
|
||||
}
|
||||
initLogBuffer();
|
||||
}
|
||||
else if (msg.what == MESSAGE_NEWTS)
|
||||
{
|
||||
for (AdapterDataObserver observer : observers)
|
||||
{
|
||||
observer.onChanged();
|
||||
}
|
||||
}
|
||||
else if (msg.what == MESSAGE_NEWLOGLEVEL)
|
||||
{
|
||||
initCurrentMessages();
|
||||
|
||||
for (AdapterDataObserver observer : observers)
|
||||
{
|
||||
observer.onChanged();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param logmessage
|
||||
* @return True if the current entries have changed
|
||||
*/
|
||||
private boolean addLogMessage(LogItem logmessage)
|
||||
{
|
||||
allEntries.add(logmessage);
|
||||
|
||||
if (allEntries.size() > MAX_STORED_LOG_ENTRIES)
|
||||
{
|
||||
Vector<LogItem> oldAllEntries = allEntries;
|
||||
allEntries = new Vector<LogItem>(allEntries.size());
|
||||
for (int i = 50; i < oldAllEntries.size(); i++)
|
||||
{
|
||||
allEntries.add(oldAllEntries.elementAt(i));
|
||||
}
|
||||
initCurrentMessages();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logmessage.getLogLevel().getInt() <= mLogLevel)
|
||||
{
|
||||
currentLevelEntries.add(logmessage);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public LogItem getItem(int position)
|
||||
{
|
||||
return currentLevelEntries.get(position);
|
||||
}
|
||||
|
||||
public void clearLog()
|
||||
{
|
||||
// Actually is probably called from GUI Thread as result of the user
|
||||
// pressing a button. But better safe than sorry
|
||||
VectrasStatus.clearLog();
|
||||
}
|
||||
|
||||
public void scrollToLastPosition()
|
||||
{
|
||||
// scroll para ultima mensagem
|
||||
mLinearLayoutManager.scrollToPosition(
|
||||
mLinearLayoutManager.getItemCount() - 1);
|
||||
}
|
||||
|
||||
public void setLogLevel(int level) {
|
||||
mLogLevel = level;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* LogListener
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void newLog(LogItem logMessage)
|
||||
{
|
||||
Message msg = Message.obtain();
|
||||
|
||||
assert (msg != null);
|
||||
msg.what = MESSAGE_NEWLOG;
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable("logmessage", logMessage);
|
||||
|
||||
msg.setData(bundle);
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClear()
|
||||
{
|
||||
mHandler.sendEmptyMessage(MESSAGE_CLEARLOG);
|
||||
}
|
||||
|
||||
}
|
||||
29
app/src/main/java/com/vectras/vm/data/LoginDataSource.java
Normal file
29
app/src/main/java/com/vectras/vm/data/LoginDataSource.java
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package com.vectras.vm.data;
|
||||
|
||||
import com.vectras.vm.data.model.LoggedInUser;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Class that handles authentication w/ login credentials and retrieves user information.
|
||||
*/
|
||||
public class LoginDataSource {
|
||||
|
||||
public Result<LoggedInUser> login(String username, String password) {
|
||||
|
||||
try {
|
||||
// TODO: handle loggedInUser authentication
|
||||
LoggedInUser fakeUser =
|
||||
new LoggedInUser(
|
||||
java.util.UUID.randomUUID().toString(),
|
||||
"Jane Doe");
|
||||
return new Result.Success<>(fakeUser);
|
||||
} catch (Exception e) {
|
||||
return new Result.Error(new IOException("Error logging in", e));
|
||||
}
|
||||
}
|
||||
|
||||
public void logout() {
|
||||
// TODO: revoke authentication
|
||||
}
|
||||
}
|
||||
54
app/src/main/java/com/vectras/vm/data/LoginRepository.java
Normal file
54
app/src/main/java/com/vectras/vm/data/LoginRepository.java
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package com.vectras.vm.data;
|
||||
|
||||
import com.vectras.vm.data.model.LoggedInUser;
|
||||
|
||||
/**
|
||||
* Class that requests authentication and user information from the remote data source and
|
||||
* maintains an in-memory cache of login status and user credentials information.
|
||||
*/
|
||||
public class LoginRepository {
|
||||
|
||||
private static volatile LoginRepository instance;
|
||||
|
||||
private LoginDataSource dataSource;
|
||||
|
||||
// If user credentials will be cached in local storage, it is recommended it be encrypted
|
||||
// @see https://developer.android.com/training/articles/keystore
|
||||
private LoggedInUser user = null;
|
||||
|
||||
// private constructor : singleton access
|
||||
private LoginRepository(LoginDataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
public static LoginRepository getInstance(LoginDataSource dataSource) {
|
||||
if (instance == null) {
|
||||
instance = new LoginRepository(dataSource);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public boolean isLoggedIn() {
|
||||
return user != null;
|
||||
}
|
||||
|
||||
public void logout() {
|
||||
user = null;
|
||||
dataSource.logout();
|
||||
}
|
||||
|
||||
private void setLoggedInUser(LoggedInUser user) {
|
||||
this.user = user;
|
||||
// If user credentials will be cached in local storage, it is recommended it be encrypted
|
||||
// @see https://developer.android.com/training/articles/keystore
|
||||
}
|
||||
|
||||
public Result<LoggedInUser> login(String username, String password) {
|
||||
// handle login
|
||||
Result<LoggedInUser> result = dataSource.login(username, password);
|
||||
if (result instanceof Result.Success) {
|
||||
setLoggedInUser(((Result.Success<LoggedInUser>) result).getData());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
48
app/src/main/java/com/vectras/vm/data/Result.java
Normal file
48
app/src/main/java/com/vectras/vm/data/Result.java
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
package com.vectras.vm.data;
|
||||
|
||||
/**
|
||||
* A generic class that holds a result success w/ data or an error exception.
|
||||
*/
|
||||
public class Result<T> {
|
||||
// hide the private constructor to limit subclass types (Success, Error)
|
||||
private Result() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this instanceof Result.Success) {
|
||||
Result.Success success = (Result.Success) this;
|
||||
return "Success[data=" + success.getData().toString() + "]";
|
||||
} else if (this instanceof Result.Error) {
|
||||
Result.Error error = (Result.Error) this;
|
||||
return "Error[exception=" + error.getError().toString() + "]";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Success sub-class
|
||||
public final static class Success<T> extends Result {
|
||||
private T data;
|
||||
|
||||
public Success(T data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
|
||||
// Error sub-class
|
||||
public final static class Error extends Result {
|
||||
private Exception error;
|
||||
|
||||
public Error(Exception error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public Exception getError() {
|
||||
return this.error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.vectras.vm.data.model;
|
||||
|
||||
/**
|
||||
* Data class that captures user information for logged in users retrieved from LoginRepository
|
||||
*/
|
||||
public class LoggedInUser {
|
||||
|
||||
private String userId;
|
||||
private String displayName;
|
||||
|
||||
public LoggedInUser(String userId, String displayName) {
|
||||
this.userId = userId;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
}
|
||||
211
app/src/main/java/com/vectras/vm/logger/LogItem.java
Normal file
211
app/src/main/java/com/vectras/vm/logger/LogItem.java
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2016 Arne Schwabe
|
||||
* Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
|
||||
*/
|
||||
|
||||
package com.vectras.vm.logger;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.vectras.vm.R;
|
||||
import android.content.Context;
|
||||
import java.util.Locale;
|
||||
import java.util.UnknownFormatConversionException;
|
||||
import java.util.FormatFlagsConversionMismatchException;
|
||||
import android.annotation.SuppressLint;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import android.content.pm.PackageManager;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import android.content.pm.Signature;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Arrays;
|
||||
import android.content.pm.PackageInfo;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
/**
|
||||
* Created by arne on 24.04.16.
|
||||
*/
|
||||
public class LogItem implements Parcelable {
|
||||
|
||||
private Object[] mArgs = null;
|
||||
private String mMessage = null;
|
||||
private int mResourceId;
|
||||
// Default log priority
|
||||
VectrasStatus.LogLevel mLevel = VectrasStatus.LogLevel.INFO;
|
||||
private long logtime = System.currentTimeMillis();
|
||||
private int mVerbosityLevel = -1;
|
||||
|
||||
public LogItem(int resId, Object... args) {
|
||||
mResourceId = resId;
|
||||
mArgs = args;
|
||||
}
|
||||
|
||||
public LogItem(VectrasStatus.LogLevel loglevel, int verblevel, String msg) {
|
||||
mLevel = loglevel;
|
||||
mMessage = msg;
|
||||
mVerbosityLevel = verblevel;
|
||||
}
|
||||
|
||||
public LogItem(VectrasStatus.LogLevel level, int resId, Object... args) {
|
||||
mLevel = level;
|
||||
mResourceId = resId;
|
||||
mArgs = args;
|
||||
}
|
||||
|
||||
public LogItem(VectrasStatus.LogLevel loglevel, String msg) {
|
||||
mLevel = loglevel;
|
||||
mMessage = msg;
|
||||
}
|
||||
|
||||
|
||||
public LogItem(VectrasStatus.LogLevel loglevel, int ressourceId) {
|
||||
mResourceId = ressourceId;
|
||||
mLevel = loglevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getString(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeArray(mArgs);
|
||||
dest.writeString(mMessage);
|
||||
dest.writeInt(mResourceId);
|
||||
dest.writeInt(mLevel.getInt());
|
||||
|
||||
dest.writeLong(logtime);
|
||||
}
|
||||
|
||||
public LogItem(Parcel in) {
|
||||
mArgs = in.readArray(Object.class.getClassLoader());
|
||||
mMessage = in.readString();
|
||||
mResourceId = in.readInt();
|
||||
mLevel = VectrasStatus.LogLevel.getEnumByValue(in.readInt());
|
||||
logtime = in.readLong();
|
||||
}
|
||||
|
||||
public static final Creator<LogItem> CREATOR
|
||||
= new Creator<LogItem>() {
|
||||
public LogItem createFromParcel(Parcel in) {
|
||||
return new LogItem(in);
|
||||
}
|
||||
|
||||
public LogItem[] newArray(int size) {
|
||||
return new LogItem[size];
|
||||
}
|
||||
};
|
||||
|
||||
public VectrasStatus.LogLevel getLogLevel() {
|
||||
return mLevel;
|
||||
}
|
||||
|
||||
public long getLogtime() {
|
||||
return logtime;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return mMessage;
|
||||
}
|
||||
|
||||
public String getString(Context c) {
|
||||
try {
|
||||
if (mMessage != null) {
|
||||
return mMessage;
|
||||
} else {
|
||||
if (c != null) {
|
||||
if (mResourceId == R.string.app_name)
|
||||
return getAppInfoString(c);
|
||||
else if (mArgs == null)
|
||||
return c.getString(mResourceId);
|
||||
else
|
||||
return c.getString(mResourceId, mArgs);
|
||||
} else {
|
||||
String str = String.format(Locale.ENGLISH, "Log (no context) resid %d", mResourceId);
|
||||
if (mArgs != null)
|
||||
str += join("|", mArgs);
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
} catch (UnknownFormatConversionException e) {
|
||||
if (c != null)
|
||||
throw new UnknownFormatConversionException(e.getLocalizedMessage() + getString(null));
|
||||
else
|
||||
throw e;
|
||||
} catch (java.util.FormatFlagsConversionMismatchException e) {
|
||||
if (c != null)
|
||||
throw new FormatFlagsConversionMismatchException(e.getLocalizedMessage() + getString(null), e.getConversion());
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
//private String listb = "";
|
||||
|
||||
// The lint is wrong here
|
||||
@SuppressLint("StringFormatMatches")
|
||||
private String getAppInfoString(Context c) {
|
||||
c.getPackageManager();
|
||||
String apksign = "error getting package signature";
|
||||
|
||||
String version = "error getting version";
|
||||
try {
|
||||
@SuppressLint("PackageManagerGetSignatures")
|
||||
Signature raw = c.getPackageManager().getPackageInfo(c.getPackageName(), PackageManager.GET_SIGNATURES).signatures[0];
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(raw.toByteArray()));
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-1");
|
||||
byte[] der = cert.getEncoded();
|
||||
md.update(der);
|
||||
byte[] digest = md.digest();
|
||||
if (!Arrays.equals(digest, VectrasStatus.oficialkey) && !Arrays.equals(digest, VectrasStatus.oficialdebugkey))
|
||||
apksign = "";
|
||||
PackageInfo packageinfo = c.getPackageManager().getPackageInfo(c.getPackageName(), 0);
|
||||
version = String.format("%s Projeto %d", packageinfo.versionName, packageinfo.versionCode);
|
||||
|
||||
} catch (PackageManager.NameNotFoundException | CertificateException |
|
||||
NoSuchAlgorithmException ignored) {
|
||||
}
|
||||
|
||||
/* Object[] argsext = Arrays.copyOf(mArgs, mArgs.length);
|
||||
argsext[argsext.length - 1] = apksign;
|
||||
argsext[argsext.length - 2] = version;*/
|
||||
|
||||
return c.getString(R.string.app_name, version, apksign);
|
||||
|
||||
}
|
||||
|
||||
// TextUtils.join will cause not macked exeception in tests ....
|
||||
public static String join(CharSequence delimiter, Object[] tokens) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean firstTime = true;
|
||||
for (Object token : tokens) {
|
||||
if (firstTime) {
|
||||
firstTime = false;
|
||||
} else {
|
||||
sb.append(delimiter);
|
||||
}
|
||||
sb.append(token);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public int getVerbosityLevel() {
|
||||
if (mVerbosityLevel == -1) {
|
||||
// Hack:
|
||||
// For message not from OpenVPN, report the status level as log level
|
||||
return mLevel.getInt();
|
||||
}
|
||||
return mVerbosityLevel;
|
||||
}
|
||||
}
|
||||
33
app/src/main/java/com/vectras/vm/logger/VMStatus.java
Normal file
33
app/src/main/java/com/vectras/vm/logger/VMStatus.java
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package com.vectras.vm.logger;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public enum VMStatus implements Parcelable {
|
||||
V_STARTVM,
|
||||
V_STOPVM,
|
||||
UNKNOWN_LEVEL;
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(ordinal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static final Creator<VMStatus> CREATOR = new Creator<VMStatus>() {
|
||||
@Override
|
||||
public VMStatus createFromParcel(Parcel in) {
|
||||
return VMStatus.values()[in.readInt()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public VMStatus[] newArray(int size) {
|
||||
return new VMStatus[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
355
app/src/main/java/com/vectras/vm/logger/VectrasStatus.java
Normal file
355
app/src/main/java/com/vectras/vm/logger/VectrasStatus.java
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
package com.vectras.vm.logger;
|
||||
|
||||
import static android.provider.Settings.System.getString;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Vector;
|
||||
import android.os.Build;
|
||||
import com.vectras.vm.R;
|
||||
import android.content.Intent;
|
||||
import android.content.Context;
|
||||
import android.os.Message;
|
||||
import java.io.File;
|
||||
import android.os.HandlerThread;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
||||
public class VectrasStatus
|
||||
{
|
||||
private static final LinkedList<LogItem> logbuffer;
|
||||
|
||||
private static Vector<LogListener> logListener;
|
||||
private static Vector<StateListener> stateListener;
|
||||
|
||||
private static VMStatus mLastLevel = VMStatus.V_STOPVM;
|
||||
|
||||
private static String mLaststatemsg = "";
|
||||
private static String mLaststate = "NOPROCESS";
|
||||
private static int mLastStateresid = R.string.noproccesses;
|
||||
private static Intent mLastIntent = null;
|
||||
|
||||
|
||||
static final int MAXLOGENTRIES = 1000;
|
||||
|
||||
public static boolean isVMActive() {
|
||||
return mLastLevel != VMStatus.V_STOPVM && !(mLastLevel == VMStatus.V_STOPVM);
|
||||
}
|
||||
|
||||
public static String getLastState() {
|
||||
return mLaststate;
|
||||
}
|
||||
|
||||
public static String getLastCleanLogMessage(Context c) {
|
||||
String message = mLaststatemsg;
|
||||
switch (mLastLevel) {
|
||||
case V_STARTVM:
|
||||
String[] parts = mLaststatemsg.split(",");
|
||||
if (parts.length >= 7)
|
||||
message = String.format(Locale.US, "%s %s", parts[1], parts[6]);
|
||||
break;
|
||||
}
|
||||
|
||||
while (message.endsWith(","))
|
||||
message = message.substring(0, message.length() - 1);
|
||||
|
||||
String status = mLaststate;
|
||||
if (status.equals("NOPROCESS"))
|
||||
return message;
|
||||
|
||||
String prefix = c.getString(mLastStateresid);
|
||||
if (mLastStateresid == R.string.unknownstate)
|
||||
message = status + message;
|
||||
if (message.length() > 0)
|
||||
prefix += ": ";
|
||||
|
||||
return prefix + message;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static enum LogLevel {
|
||||
|
||||
INFO(2),
|
||||
ERROR(-2),
|
||||
WARNING(1),
|
||||
VERBOSE(3),
|
||||
DEBUG(4);
|
||||
|
||||
protected int mValue;
|
||||
|
||||
LogLevel(int value) {
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public int getInt() {
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public static LogLevel getEnumByValue(int value) {
|
||||
switch (value) {
|
||||
case 2:
|
||||
return INFO;
|
||||
case -2:
|
||||
return ERROR;
|
||||
case 1:
|
||||
return WARNING;
|
||||
case 3:
|
||||
return VERBOSE;
|
||||
case 4:
|
||||
return DEBUG;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// keytool -printcert -jarfile de.blinkt.openvpn_85.apk
|
||||
// tudo ok, certificado da Playstore
|
||||
static final byte[] oficialkey = {93, -72, 88, 103, -128, 115, -1, -47, 120, 113, 98, -56, 12, -56, 52, -62, 95, -2, -114, 95};
|
||||
// já atualizado, slipk certificado
|
||||
static final byte[] oficialdebugkey = {-41, 73, 58, 102, -81, -27, -120, 45, -56, -3, 53, -49, 119, -97, -20, -80, 65, 68, -72, -22};
|
||||
|
||||
static {
|
||||
logbuffer = new LinkedList<>();
|
||||
logListener = new Vector<>();
|
||||
stateListener = new Vector<>();
|
||||
|
||||
logInformation();
|
||||
}
|
||||
|
||||
|
||||
public synchronized static void clearLog() {
|
||||
logbuffer.clear();
|
||||
logInformation();
|
||||
logInfo("LOGS CLEARED!");
|
||||
|
||||
for (LogListener li : logListener) {
|
||||
li.onClear();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized static LogItem[] getlogbuffer() {
|
||||
|
||||
// The stoned way of java to return an array from a vector
|
||||
// brought to you by eclipse auto complete
|
||||
return logbuffer.toArray(new LogItem[logbuffer.size()]);
|
||||
}
|
||||
|
||||
private static void logInformation() {
|
||||
logInfo(R.string.app_name);
|
||||
logInfo(R.string.app_version);
|
||||
logInfo("MOBILE MODEL: " + Build.MODEL);
|
||||
logInfo("ANDROID VERSION: " + Build.VERSION.SDK_INT);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Listeners
|
||||
*/
|
||||
|
||||
public interface LogListener {
|
||||
void newLog(LogItem logItem);
|
||||
void onClear();
|
||||
}
|
||||
|
||||
public interface StateListener {
|
||||
void updateState(String state, String logMessage, int localizedResId, VMStatus level, Intent intent);
|
||||
}
|
||||
|
||||
public synchronized static void addLogListener(LogListener ll) {
|
||||
if (!logListener.contains(ll)) {
|
||||
logListener.add(ll);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized static void removeLogListener(LogListener ll) {
|
||||
if (logListener.contains(ll)) {
|
||||
logListener.remove(ll);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized static void addStateListener(StateListener sl) {
|
||||
if (!stateListener.contains(sl)) {
|
||||
stateListener.add(sl);
|
||||
if (mLaststate != null)
|
||||
sl.updateState(mLaststate, mLaststatemsg, mLastStateresid, mLastLevel, mLastIntent);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized static void removeStateListener(StateListener sl) {
|
||||
if (stateListener.contains(sl)) {
|
||||
stateListener.remove(sl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* State
|
||||
*/
|
||||
|
||||
public static final String
|
||||
V_STARTVM = "STARTING VM",
|
||||
V_STOPVM = "STOPPING VM";
|
||||
|
||||
public static int getLocalizedState(String state) {
|
||||
switch (state) {
|
||||
case V_STARTVM:
|
||||
return R.string.startvm;
|
||||
case V_STOPVM:
|
||||
return R.string.stopvm;
|
||||
}
|
||||
return R.string.unknownstate;
|
||||
}
|
||||
|
||||
private static VMStatus getLevel(String state) {
|
||||
String[] noreplyet = {V_STARTVM, V_STOPVM};
|
||||
String[] reply = {V_STARTVM, V_STOPVM};
|
||||
String[] startedvm = {V_STARTVM};
|
||||
String[] stoppedvm = {V_STOPVM};
|
||||
|
||||
for (String x : noreplyet)
|
||||
if (state.equals(x))
|
||||
return VMStatus.V_STARTVM;
|
||||
|
||||
for (String x : reply)
|
||||
if (state.equals(x))
|
||||
return VMStatus.V_STOPVM;
|
||||
|
||||
for (String x : startedvm)
|
||||
if (state.equals(x))
|
||||
return VMStatus.V_STARTVM;
|
||||
|
||||
for (String x : stoppedvm)
|
||||
if (state.equals(x))
|
||||
return VMStatus.V_STOPVM;
|
||||
|
||||
return VMStatus.UNKNOWN_LEVEL;
|
||||
}
|
||||
|
||||
public static void updateStateString(String state, String msg) {
|
||||
int rid = getLocalizedState(state);
|
||||
VMStatus level = getLevel(state);
|
||||
updateStateString(state, msg, rid, level);
|
||||
}
|
||||
|
||||
public synchronized static void updateStateString(String state, String msg, int resid, VMStatus level)
|
||||
{
|
||||
updateStateString(state, msg, resid, level, null);
|
||||
}
|
||||
|
||||
public synchronized static void updateStateString(String state, String msg, int resid, VMStatus level, Intent intent) {
|
||||
// Workound for OpenVPN doing AUTH and wait and being startedvm
|
||||
// Simply ignore these state
|
||||
/*if (mLastLevel == VMStatus.LEVEL_CONNECTED &&
|
||||
(state.equals(SSH_AUTHENTICATING))) {
|
||||
newLogItem(new LogItem((LogLevel.DEBUG), String.format("Ignoring SocksHttp Status in CONNECTED state (%s->%s): %s", state, level.toString(), msg)));
|
||||
return;
|
||||
}*/
|
||||
|
||||
mLaststate = state;
|
||||
mLaststatemsg = msg;
|
||||
mLastStateresid = resid;
|
||||
mLastLevel = level;
|
||||
mLastIntent = intent;
|
||||
|
||||
|
||||
for (StateListener sl : stateListener) {
|
||||
sl.updateState(state, msg, resid, level, intent);
|
||||
}
|
||||
|
||||
//newLogItem(new LogItem((LogLevel.DEBUG), String.format("SocksHttp Novo Status (%s->%s): %s",state,level.toString(),msg)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* NewLog
|
||||
*/
|
||||
|
||||
static void newLogItem(LogItem logItem) {
|
||||
newLogItem(logItem, false);
|
||||
}
|
||||
|
||||
synchronized static void newLogItem(LogItem logItem, boolean cachedLine) {
|
||||
if (cachedLine) {
|
||||
logbuffer.addFirst(logItem);
|
||||
} else {
|
||||
logbuffer.addLast(logItem);
|
||||
}
|
||||
|
||||
if (logbuffer.size() > MAXLOGENTRIES + MAXLOGENTRIES / 2) {
|
||||
while (logbuffer.size() > MAXLOGENTRIES)
|
||||
logbuffer.removeFirst();
|
||||
}
|
||||
|
||||
for (LogListener ll : logListener) {
|
||||
ll.newLog(logItem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Logger static methods
|
||||
*/
|
||||
|
||||
public static void logException(String context, String e) {
|
||||
logException(LogLevel.ERROR, context, e);
|
||||
}
|
||||
|
||||
public static void logException(LogLevel ll, String context, String e) {
|
||||
|
||||
LogItem li;
|
||||
|
||||
if (context != null)
|
||||
li = new LogItem(ll, String.format("%s: %s", context, e));
|
||||
else
|
||||
li = new LogItem(ll, String.format("Error: %s", e));
|
||||
|
||||
newLogItem(li);
|
||||
}
|
||||
|
||||
public static void logException(Exception e) {
|
||||
logException(LogLevel.ERROR, null, e.getMessage());
|
||||
}
|
||||
|
||||
public static void logInfo(String message) {
|
||||
newLogItem(new LogItem(LogLevel.INFO, message));
|
||||
}
|
||||
|
||||
public static void logDebug(String message) {
|
||||
newLogItem(new LogItem(LogLevel.DEBUG, message));
|
||||
}
|
||||
|
||||
public static void logInfo(int resourceId, Object... args) {
|
||||
newLogItem(new LogItem(LogLevel.INFO, resourceId, args));
|
||||
}
|
||||
|
||||
public static void logDebug(int resourceId, Object... args) {
|
||||
newLogItem(new LogItem(LogLevel.DEBUG, resourceId, args));
|
||||
}
|
||||
|
||||
public static void logError(String msg) {
|
||||
newLogItem(new LogItem(LogLevel.ERROR, msg));
|
||||
}
|
||||
|
||||
public static void logWarning(int resourceId, Object... args) {
|
||||
newLogItem(new LogItem(LogLevel.WARNING, resourceId, args));
|
||||
}
|
||||
|
||||
public static void logWarning(String msg) {
|
||||
newLogItem(new LogItem(LogLevel.WARNING, msg));
|
||||
}
|
||||
|
||||
public static void logError(int resourceId) {
|
||||
newLogItem(new LogItem(LogLevel.ERROR, resourceId));
|
||||
}
|
||||
|
||||
public static void logError(int resourceId, Object... args) {
|
||||
newLogItem(new LogItem(LogLevel.ERROR, resourceId, args));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
package com.vectras.vm.ui.login;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.google.android.gms.auth.api.Auth;
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInResult;
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.SignInButton;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.OptionalPendingResult;
|
||||
import com.google.android.gms.common.api.ResultCallback;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
import com.vectras.vm.R;
|
||||
import com.vectras.vm.RomsManagerActivity;
|
||||
|
||||
/**
|
||||
* Activity to demonstrate basic retrieval of the Google user's ID, email address, and basic
|
||||
* profile.
|
||||
*/
|
||||
public class GoogleSignInActivity extends AppCompatActivity implements
|
||||
GoogleApiClient.OnConnectionFailedListener,
|
||||
View.OnClickListener {
|
||||
|
||||
private static final String TAG = "GoogleSignInActivity";
|
||||
private static final int RC_SIGN_IN = 9001;
|
||||
|
||||
private GoogleApiClient mGoogleApiClient;
|
||||
private TextView mStatusTextView;
|
||||
private ProgressDialog mProgressDialog;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_google_sign_in);
|
||||
|
||||
// Views
|
||||
mStatusTextView = (TextView) findViewById(R.id.status);
|
||||
|
||||
// Button listeners
|
||||
findViewById(R.id.sign_in_button).setOnClickListener(this);
|
||||
findViewById(R.id.sign_out_button).setOnClickListener(this);
|
||||
findViewById(R.id.disconnect_button).setOnClickListener(this);
|
||||
|
||||
// [START configure_signin]
|
||||
// Configure sign-in to request the user's ID, email address, and basic
|
||||
// profile. ID and basic profile are included in DEFAULT_SIGN_IN.
|
||||
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
|
||||
.requestEmail()
|
||||
.build();
|
||||
// [END configure_signin]
|
||||
|
||||
// [START build_client]
|
||||
// Build a GoogleApiClient with access to the Google Sign-In API and the
|
||||
// options specified by gso.
|
||||
mGoogleApiClient = new GoogleApiClient.Builder(this)
|
||||
.enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
|
||||
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
|
||||
.build();
|
||||
// [END build_client]
|
||||
|
||||
// [START customize_button]
|
||||
// Set the dimensions of the sign-in button.
|
||||
SignInButton signInButton = (SignInButton) findViewById(R.id.sign_in_button);
|
||||
signInButton.setSize(SignInButton.SIZE_STANDARD);
|
||||
// [END customize_button]
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
OptionalPendingResult<GoogleSignInResult> opr = Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient);
|
||||
if (opr.isDone()) {
|
||||
// If the user's cached credentials are valid, the OptionalPendingResult will be "done"
|
||||
// and the GoogleSignInResult will be available instantly.
|
||||
Log.d(TAG, "Got cached sign-in");
|
||||
GoogleSignInResult result = opr.get();
|
||||
handleSignInResult(result);
|
||||
} else {
|
||||
// If the user has not previously signed in on this device or the sign-in has expired,
|
||||
// this asynchronous branch will attempt to sign in the user silently. Cross-device
|
||||
// single sign-on will occur in this branch.
|
||||
showProgressDialog();
|
||||
opr.setResultCallback(new ResultCallback<GoogleSignInResult>() {
|
||||
@Override
|
||||
public void onResult(GoogleSignInResult googleSignInResult) {
|
||||
hideProgressDialog();
|
||||
handleSignInResult(googleSignInResult);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// [START onActivityResult]
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
// Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...);
|
||||
if (requestCode == RC_SIGN_IN) {
|
||||
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
|
||||
handleSignInResult(result);
|
||||
}
|
||||
}
|
||||
// [END onActivityResult]
|
||||
|
||||
// [START handleSignInResult]
|
||||
private void handleSignInResult(GoogleSignInResult result) {
|
||||
Log.d(TAG, "handleSignInResult:" + result.isSuccess());
|
||||
if (result.isSuccess()) {
|
||||
// Signed in successfully, show authenticated UI.
|
||||
GoogleSignInAccount acct = result.getSignInAccount();
|
||||
mStatusTextView.setText(getString(Integer.parseInt(acct.getDisplayName())));
|
||||
updateUI(true);
|
||||
} else {
|
||||
// Signed out, show unauthenticated UI.
|
||||
updateUI(false);
|
||||
}
|
||||
}
|
||||
// [END handleSignInResult]
|
||||
|
||||
// [START signIn]
|
||||
private void signIn() {
|
||||
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
|
||||
startActivityForResult(signInIntent, RC_SIGN_IN);
|
||||
}
|
||||
// [END signIn]
|
||||
|
||||
// [START signOut]
|
||||
private void signOut() {
|
||||
Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback(
|
||||
new ResultCallback<Status>() {
|
||||
@Override
|
||||
public void onResult(Status status) {
|
||||
// [START_EXCLUDE]
|
||||
updateUI(false);
|
||||
// [END_EXCLUDE]
|
||||
}
|
||||
});
|
||||
}
|
||||
// [END signOut]
|
||||
|
||||
// [START revokeAccess]
|
||||
private void revokeAccess() {
|
||||
Auth.GoogleSignInApi.revokeAccess(mGoogleApiClient).setResultCallback(
|
||||
new ResultCallback<Status>() {
|
||||
@Override
|
||||
public void onResult(Status status) {
|
||||
// [START_EXCLUDE]
|
||||
updateUI(false);
|
||||
// [END_EXCLUDE]
|
||||
}
|
||||
});
|
||||
}
|
||||
// [END revokeAccess]
|
||||
|
||||
@Override
|
||||
public void onConnectionFailed(ConnectionResult connectionResult) {
|
||||
// An unresolvable error has occurred and Google APIs (including Sign-In) will not
|
||||
// be available.
|
||||
Log.d(TAG, "onConnectionFailed:" + connectionResult);
|
||||
}
|
||||
|
||||
private void showProgressDialog() {
|
||||
if (mProgressDialog == null) {
|
||||
mProgressDialog = new ProgressDialog(this);
|
||||
mProgressDialog.setMessage("Loading...");
|
||||
mProgressDialog.setIndeterminate(true);
|
||||
}
|
||||
|
||||
mProgressDialog.show();
|
||||
}
|
||||
|
||||
private void hideProgressDialog() {
|
||||
if (mProgressDialog != null && mProgressDialog.isShowing()) {
|
||||
mProgressDialog.hide();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUI(boolean signedIn) {
|
||||
if (signedIn)
|
||||
startActivity(new Intent(GoogleSignInActivity.this, RomsManagerActivity.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int id = v.getId();
|
||||
if (id == R.id.sign_in_button) {
|
||||
signIn();
|
||||
} else if (id == R.id.sign_out_button) {
|
||||
signOut();
|
||||
} else if (id == R.id.disconnect_button) {
|
||||
revokeAccess();
|
||||
}
|
||||
}
|
||||
}
|
||||
150
app/src/main/java/com/vectras/vm/ui/login/LoginActivity.java
Normal file
150
app/src/main/java/com/vectras/vm/ui/login/LoginActivity.java
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
package com.vectras.vm.ui.login;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.gms.auth.api.Auth;
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInResult;
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.ResultCallback;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
import com.google.android.gms.tasks.OnCompleteListener;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import com.google.firebase.auth.AuthResult;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.auth.FirebaseUser;
|
||||
import com.google.firebase.auth.UserProfileChangeRequest;
|
||||
import com.vectras.vm.R;
|
||||
import com.vectras.vm.RomsManagerActivity;
|
||||
|
||||
public class LoginActivity extends AppCompatActivity {
|
||||
|
||||
private FirebaseAuth mAuth;
|
||||
private TextView mStatusTextView;
|
||||
// ...
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_login);
|
||||
|
||||
MaterialButton signinButton = findViewById(R.id.signinBtn);
|
||||
|
||||
MaterialButton signupButton = findViewById(R.id.signupBtn);
|
||||
|
||||
MaterialButton signwithGoogleButton = findViewById(R.id.signwithGoogleBtn);
|
||||
|
||||
TextInputEditText usernameEditText = findViewById(R.id.username);
|
||||
|
||||
TextInputEditText passwordEditText = findViewById(R.id.password);
|
||||
|
||||
mStatusTextView = findViewById(R.id.errorTxt);
|
||||
|
||||
// Initialize Firebase Auth
|
||||
mAuth = FirebaseAuth.getInstance();
|
||||
TextWatcher afterTextChangedListener = new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (usernameEditText.getText().toString().trim().length() > 0) {
|
||||
signinButton.setEnabled(true);
|
||||
} else {
|
||||
signinButton.setEnabled(false);
|
||||
}
|
||||
if (passwordEditText.getText().toString().trim().length() > 0) {
|
||||
signinButton.setEnabled(true);
|
||||
} else {
|
||||
signinButton.setEnabled(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
usernameEditText.addTextChangedListener(afterTextChangedListener);
|
||||
passwordEditText.addTextChangedListener(afterTextChangedListener);
|
||||
passwordEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
signinButton.setOnClickListener(v -> mAuth.signInWithEmailAndPassword(usernameEditText.getText().toString(), passwordEditText.getText().toString())
|
||||
.addOnCompleteListener(LoginActivity.this, (OnCompleteListener<AuthResult>) task -> {
|
||||
if (task.isSuccessful()) {
|
||||
// Sign in success, update UI with the signed-in user's information
|
||||
FirebaseUser user = mAuth.getCurrentUser();
|
||||
updateUI(user);
|
||||
} else {
|
||||
|
||||
mStatusTextView.setText(task.getException().toString());
|
||||
// If sign in fails, display a message to the user.
|
||||
Toast.makeText(LoginActivity.this, "Authentication failed.",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
updateUI(null);
|
||||
}
|
||||
}));
|
||||
signupButton.setOnClickListener(v -> startActivity(new Intent(this, SignupActivity.class)));
|
||||
signwithGoogleButton.setOnClickListener(v -> startActivity(new Intent(this, GoogleSignInActivity.class)));
|
||||
|
||||
}
|
||||
|
||||
public void updateUI(Object USER) {
|
||||
if (USER != null) {
|
||||
FirebaseAuth auth = FirebaseAuth.getInstance();
|
||||
FirebaseUser user = auth.getCurrentUser();
|
||||
|
||||
if (user != null && !user.isEmailVerified()) {
|
||||
user.sendEmailVerification()
|
||||
.addOnCompleteListener(new OnCompleteListener<Void>() {
|
||||
@Override
|
||||
public void onComplete(@NonNull Task<Void> task) {
|
||||
startActivity(new Intent(LoginActivity.this, RomsManagerActivity.class));
|
||||
}
|
||||
});
|
||||
}
|
||||
if (user != null && user.isEmailVerified())
|
||||
startActivity(new Intent(LoginActivity.this, RomsManagerActivity.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
// Check if user is signed in (non-null) and update UI accordingly.
|
||||
FirebaseUser currentUser = mAuth.getCurrentUser();
|
||||
if (currentUser != null) {
|
||||
currentUser.reload();
|
||||
}
|
||||
updateUI(currentUser);
|
||||
}
|
||||
}
|
||||
140
app/src/main/java/com/vectras/vm/ui/login/SignupActivity.java
Normal file
140
app/src/main/java/com/vectras/vm/ui/login/SignupActivity.java
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
package com.vectras.vm.ui.login;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.gms.tasks.OnCompleteListener;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.auth.FirebaseUser;
|
||||
import com.google.firebase.auth.UserProfileChangeRequest;
|
||||
import com.vectras.vm.R;
|
||||
import com.vectras.vm.RomsManagerActivity;
|
||||
|
||||
public class SignupActivity extends AppCompatActivity {
|
||||
|
||||
private FirebaseAuth mAuth;
|
||||
private TextView mStatusTextView;
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_signup);
|
||||
|
||||
MaterialButton signupButton = findViewById(R.id.signupBtn);
|
||||
|
||||
TextInputEditText fullNameEditText = findViewById(R.id.fullName);
|
||||
|
||||
TextInputEditText usernameEditText = findViewById(R.id.username);
|
||||
|
||||
TextInputEditText passwordEditText = findViewById(R.id.password);
|
||||
|
||||
mStatusTextView = findViewById(R.id.errorTxt);
|
||||
|
||||
// Initialize Firebase Auth
|
||||
mAuth = FirebaseAuth.getInstance();
|
||||
TextWatcher afterTextChangedListener = new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (usernameEditText.getText().toString().trim().length() > 0) {
|
||||
signupButton.setEnabled(true);
|
||||
} else {
|
||||
signupButton.setEnabled(false);
|
||||
}
|
||||
if (passwordEditText.getText().toString().trim().length() > 0) {
|
||||
signupButton.setEnabled(true);
|
||||
} else {
|
||||
signupButton.setEnabled(false);
|
||||
}
|
||||
if (fullNameEditText.getText().toString().trim().length() > 0) {
|
||||
signupButton.setEnabled(true);
|
||||
} else {
|
||||
signupButton.setEnabled(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
fullNameEditText.addTextChangedListener(afterTextChangedListener);
|
||||
usernameEditText.addTextChangedListener(afterTextChangedListener);
|
||||
passwordEditText.addTextChangedListener(afterTextChangedListener);
|
||||
passwordEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
signupButton.setOnClickListener(v -> mAuth.createUserWithEmailAndPassword(usernameEditText.getText().toString(), passwordEditText.getText().toString())
|
||||
.addOnCompleteListener(SignupActivity.this, task -> {
|
||||
if (task.isSuccessful()) {
|
||||
// Sign in success, update UI with the signed-in user's information
|
||||
Log.d("SIGNUP", "createUserWithEmail:success");
|
||||
FirebaseUser user = mAuth.getCurrentUser();
|
||||
updateUI(user);
|
||||
} else {
|
||||
// If sign in fails, display a message to the user.
|
||||
Log.w("SIGNUP", "createUserWithEmail:failure", task.getException());
|
||||
|
||||
mStatusTextView.setText(task.getException().toString());
|
||||
|
||||
Toast.makeText(SignupActivity.this, "Authentication failed.",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
updateUI(null);
|
||||
}
|
||||
}));
|
||||
}
|
||||
public void updateUI(Object USER) {
|
||||
if (USER != null) {
|
||||
FirebaseAuth auth = FirebaseAuth.getInstance();
|
||||
FirebaseUser user = auth.getCurrentUser();
|
||||
|
||||
if (user != null && !user.isEmailVerified()) {
|
||||
TextInputEditText fullNameEditText = findViewById(R.id.fullName);
|
||||
UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder()
|
||||
.setDisplayName(String.valueOf(fullNameEditText.getText()))
|
||||
.build();
|
||||
|
||||
user.updateProfile(profileUpdates)
|
||||
.addOnCompleteListener(new OnCompleteListener<Void>() {
|
||||
@Override
|
||||
public void onComplete(@NonNull Task<Void> task) {
|
||||
if (task.isSuccessful()) {
|
||||
user.sendEmailVerification()
|
||||
.addOnCompleteListener(new OnCompleteListener<Void>() {
|
||||
@Override
|
||||
public void onComplete(@NonNull Task<Void> task) {
|
||||
startActivity(new Intent(SignupActivity.this, RomsManagerActivity.class));
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
81
app/src/main/java/com/vectras/vm/utils/AppUpdater.java
Normal file
81
app/src/main/java/com/vectras/vm/utils/AppUpdater.java
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
package com.vectras.vm.utils;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import com.vectras.vm.AppConfig;
|
||||
import com.vectras.vm.R;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
public class AppUpdater extends AsyncTask<String, String, String> {
|
||||
|
||||
private Context context;
|
||||
private OnUpdateListener listener;
|
||||
private ProgressDialog progressDialog;
|
||||
private boolean isOnCreate;
|
||||
|
||||
public AppUpdater(Context context, OnUpdateListener listener) {
|
||||
this.context = context;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void start(boolean isOnCreate) {
|
||||
this.isOnCreate = isOnCreate;
|
||||
execute();
|
||||
}
|
||||
|
||||
public interface OnUpdateListener {
|
||||
void onUpdateListener(String result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String doInBackground(String... strings) {
|
||||
try {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
URL url = new URL(AppConfig.updateJson);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setConnectTimeout(30000);
|
||||
conn.setReadTimeout(30000);
|
||||
conn.setRequestMethod("GET");
|
||||
conn.connect();
|
||||
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
String response;
|
||||
|
||||
while ((response = br.readLine()) != null) {
|
||||
sb.append(response);
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return "Error on getting data: " + e.getMessage();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
if (isOnCreate) {
|
||||
progressDialog = new ProgressDialog(context, R.style.MainDialogTheme);
|
||||
progressDialog.setMessage("Please wait for the check");
|
||||
progressDialog.setTitle("Looking for Update");
|
||||
progressDialog.setCancelable(false);
|
||||
progressDialog.show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String s) {
|
||||
super.onPostExecute(s);
|
||||
if (isOnCreate && progressDialog != null) {
|
||||
progressDialog.dismiss();
|
||||
}
|
||||
if (listener != null) {
|
||||
listener.onUpdateListener(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
605
app/src/main/java/com/vectras/vm/utils/FileUtils.java
Normal file
605
app/src/main/java/com/vectras/vm/utils/FileUtils.java
Normal file
|
|
@ -0,0 +1,605 @@
|
|||
package com.vectras.vm.utils;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.MediaStore;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
import com.vectras.vm.MainActivity;
|
||||
import com.vectras.vm.AppConfig;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dev
|
||||
*/
|
||||
public class FileUtils {
|
||||
|
||||
private static Uri contentUri = null;
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public static String getPath(Context context, final Uri uri) {
|
||||
// check here to KITKAT or new version
|
||||
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
||||
String selection = null;
|
||||
String[] selectionArgs = null;
|
||||
// DocumentProvider
|
||||
if (isKitKat ) {
|
||||
// ExternalStorageProvider
|
||||
|
||||
if (isExternalStorageDocument(uri)) {
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
final String[] split = docId.split(":");
|
||||
final String type = split[0];
|
||||
|
||||
String fullPath = getPathFromExtSD(split);
|
||||
if (fullPath != "") {
|
||||
return fullPath;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// DownloadsProvider
|
||||
|
||||
if (isDownloadsDocument(uri)) {
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
final String id;
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = context.getContentResolver().query(uri, new String[]{MediaStore.MediaColumns.DISPLAY_NAME}, null, null, null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
String fileName = cursor.getString(0);
|
||||
String path = Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName;
|
||||
if (!TextUtils.isEmpty(path)) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
id = DocumentsContract.getDocumentId(uri);
|
||||
if (!TextUtils.isEmpty(id)) {
|
||||
if (id.startsWith("raw:")) {
|
||||
return id.replaceFirst("raw:", "");
|
||||
}
|
||||
String[] contentUriPrefixesToTry = new String[]{
|
||||
"content://downloads/public_downloads",
|
||||
"content://downloads/my_downloads"
|
||||
};
|
||||
for (String contentUriPrefix : contentUriPrefixesToTry) {
|
||||
try {
|
||||
final Uri contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), Long.valueOf(id));
|
||||
|
||||
|
||||
return getDataColumn(context, contentUri, null, null);
|
||||
} catch (NumberFormatException e) {
|
||||
//In Android 8 and Android P the id is not a number
|
||||
return uri.getPath().replaceFirst("^/document/raw:", "").replaceFirst("^raw:", "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
final String id = DocumentsContract.getDocumentId(uri);
|
||||
|
||||
if (id.startsWith("raw:")) {
|
||||
return id.replaceFirst("raw:", "");
|
||||
}
|
||||
try {
|
||||
contentUri = ContentUris.withAppendedId(
|
||||
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (contentUri != null) {
|
||||
|
||||
return getDataColumn(context, contentUri, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MediaProvider
|
||||
if (isMediaDocument(uri)) {
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
final String[] split = docId.split(":");
|
||||
final String type = split[0];
|
||||
|
||||
Uri contentUri = null;
|
||||
|
||||
if ("image".equals(type)) {
|
||||
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if ("video".equals(type)) {
|
||||
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if ("audio".equals(type)) {
|
||||
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||
}
|
||||
selection = "_id=?";
|
||||
selectionArgs = new String[]{split[1]};
|
||||
|
||||
|
||||
return getDataColumn(context, contentUri, selection,
|
||||
selectionArgs);
|
||||
}
|
||||
|
||||
if (isGoogleDriveUri(uri)) {
|
||||
return getDriveFilePath(context, uri);
|
||||
}
|
||||
|
||||
if(isWhatsAppFile(uri)){
|
||||
return getFilePathForWhatsApp(context, uri);
|
||||
}
|
||||
|
||||
|
||||
if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||
|
||||
if (isGooglePhotosUri(uri)) {
|
||||
return uri.getLastPathSegment();
|
||||
}
|
||||
if (isGoogleDriveUri(uri)) {
|
||||
return getDriveFilePath(context, uri);
|
||||
}
|
||||
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||
{
|
||||
|
||||
// return getFilePathFromURI(context,uri);
|
||||
return copyFileToInternalStorage(context, uri,"userfiles");
|
||||
// return getRealPathFromURI(context,uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
return getDataColumn(context, uri, null, null);
|
||||
}
|
||||
|
||||
}
|
||||
if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||
return uri.getPath();
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
if(isWhatsAppFile(uri)){
|
||||
return getFilePathForWhatsApp(context, uri);
|
||||
}
|
||||
|
||||
if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||
String[] projection = {
|
||||
MediaStore.Images.Media.DATA
|
||||
};
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = context.getContentResolver()
|
||||
.query(uri, projection, selection, selectionArgs, null);
|
||||
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
|
||||
if (cursor.moveToFirst()) {
|
||||
return cursor.getString(column_index);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean fileExists(String filePath) {
|
||||
File file = new File(filePath);
|
||||
|
||||
return file.exists();
|
||||
}
|
||||
|
||||
private static String getPathFromExtSD(String[] pathData) {
|
||||
final String type = pathData[0];
|
||||
final String relativePath = "/" + pathData[1];
|
||||
String fullPath = "";
|
||||
|
||||
// on my Sony devices (4.4.4 & 5.1.1), `type` is a dynamic string
|
||||
// something like "71F8-2C0A", some kind of unique id per storage
|
||||
// don't know any API that can get the root path of that storage based on its id.
|
||||
//
|
||||
// so no "primary" type, but let the check here for other devices
|
||||
if ("primary".equalsIgnoreCase(type)) {
|
||||
fullPath = Environment.getExternalStorageDirectory() + relativePath;
|
||||
if (fileExists(fullPath)) {
|
||||
return fullPath;
|
||||
}
|
||||
}
|
||||
|
||||
// Environment.isExternalStorageRemovable() is `true` for external and internal storage
|
||||
// so we cannot relay on it.
|
||||
//
|
||||
// instead, for each possible path, check if file exists
|
||||
// we'll start with secondary storage as this could be our (physically) removable sd card
|
||||
fullPath = System.getenv("SECONDARY_STORAGE") + relativePath;
|
||||
if (fileExists(fullPath)) {
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
fullPath = System.getenv("EXTERNAL_STORAGE") + relativePath;
|
||||
if (fileExists(fullPath)) {
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
private static String getDriveFilePath(Context context, Uri uri) {
|
||||
Uri returnUri = uri;
|
||||
Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null);
|
||||
/*
|
||||
* Get the column indexes of the data in the Cursor,
|
||||
* * move to the first row in the Cursor, get the data,
|
||||
* * and display it.
|
||||
* */
|
||||
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||||
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
|
||||
returnCursor.moveToFirst();
|
||||
String name = (returnCursor.getString(nameIndex));
|
||||
String size = (Long.toString(returnCursor.getLong(sizeIndex)));
|
||||
File file = new File(context.getCacheDir(), name);
|
||||
try {
|
||||
InputStream inputStream = context.getContentResolver().openInputStream(uri);
|
||||
FileOutputStream outputStream = new FileOutputStream(file);
|
||||
int read = 0;
|
||||
int maxBufferSize = 1 * 1024 * 1024;
|
||||
int bytesAvailable = inputStream.available();
|
||||
|
||||
//int bufferSize = 1024;
|
||||
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
|
||||
|
||||
final byte[] buffers = new byte[bufferSize];
|
||||
while ((read = inputStream.read(buffers)) != -1) {
|
||||
outputStream.write(buffers, 0, read);
|
||||
}
|
||||
Log.e("File Size", "Size " + file.length());
|
||||
inputStream.close();
|
||||
outputStream.close();
|
||||
Log.e("File Path", "Path " + file.getPath());
|
||||
Log.e("File Size", "Size " + file.length());
|
||||
} catch (Exception e) {
|
||||
Log.e("Exception", e.getMessage());
|
||||
}
|
||||
return file.getPath();
|
||||
}
|
||||
|
||||
/***
|
||||
* Used for Android Q+
|
||||
* @param uri
|
||||
* @param newDirName if you want to create a directory, you can set this variable
|
||||
* @return
|
||||
*/
|
||||
private static String copyFileToInternalStorage(Context context, Uri uri, String newDirName) {
|
||||
Uri returnUri = uri;
|
||||
|
||||
Cursor returnCursor = context.getContentResolver().query(returnUri, new String[]{
|
||||
OpenableColumns.DISPLAY_NAME,OpenableColumns.SIZE
|
||||
}, null, null, null);
|
||||
|
||||
|
||||
/*
|
||||
* Get the column indexes of the data in the Cursor,
|
||||
* * move to the first row in the Cursor, get the data,
|
||||
* * and display it.
|
||||
* */
|
||||
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||||
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
|
||||
returnCursor.moveToFirst();
|
||||
String name = (returnCursor.getString(nameIndex));
|
||||
String size = (Long.toString(returnCursor.getLong(sizeIndex)));
|
||||
|
||||
File output;
|
||||
if(!newDirName.equals("")) {
|
||||
File dir = new File(context.getFilesDir() + "/" + newDirName);
|
||||
if (!dir.exists()) {
|
||||
dir.mkdir();
|
||||
}
|
||||
output = new File(context.getFilesDir() + "/" + newDirName + "/" + name);
|
||||
}
|
||||
else{
|
||||
output = new File(context.getFilesDir() + "/" + name);
|
||||
}
|
||||
try {
|
||||
InputStream inputStream = context.getContentResolver().openInputStream(uri);
|
||||
FileOutputStream outputStream = new FileOutputStream(output);
|
||||
int read = 0;
|
||||
int bufferSize = 1024;
|
||||
final byte[] buffers = new byte[bufferSize];
|
||||
while ((read = inputStream.read(buffers)) != -1) {
|
||||
outputStream.write(buffers, 0, read);
|
||||
}
|
||||
|
||||
inputStream.close();
|
||||
outputStream.close();
|
||||
|
||||
}
|
||||
catch (Exception e) {
|
||||
|
||||
Log.e("Exception", e.getMessage());
|
||||
}
|
||||
|
||||
return output.getPath();
|
||||
}
|
||||
|
||||
private static String getFilePathForWhatsApp(Context context, Uri uri){
|
||||
return copyFileToInternalStorage(context, uri,"whatsapp");
|
||||
}
|
||||
|
||||
private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
|
||||
Cursor cursor = null;
|
||||
final String column = "_data";
|
||||
final String[] projection = {column};
|
||||
|
||||
try {
|
||||
cursor = context.getContentResolver().query(uri, projection,
|
||||
selection, selectionArgs, null);
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
final int index = cursor.getColumnIndexOrThrow(column);
|
||||
return cursor.getString(index);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isExternalStorageDocument(Uri uri) {
|
||||
return "com.android.externalstorage.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
private static boolean isDownloadsDocument(Uri uri) {
|
||||
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
private static boolean isMediaDocument(Uri uri) {
|
||||
return "com.android.providers.media.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
private static boolean isGooglePhotosUri(Uri uri) {
|
||||
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
public static boolean isWhatsAppFile(Uri uri){
|
||||
return "com.whatsapp.provider.media".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
private static boolean isGoogleDriveUri(Uri uri) {
|
||||
return "com.google.android.apps.docs.storage".equals(uri.getAuthority()) || "com.google.android.apps.docs.storage.legacy".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
|
||||
public String LoadFile(Activity activity, String fileName, boolean loadFromRawFolder) throws IOException {
|
||||
// Create a InputStream to read the file into
|
||||
InputStream iS;
|
||||
if (loadFromRawFolder) {
|
||||
// get the resource id from the file name
|
||||
int rID = activity.getResources().getIdentifier(getClass().getPackage().getName() + ":raw/" + fileName,
|
||||
null, null);
|
||||
// get the file as a stream
|
||||
iS = activity.getResources().openRawResource(rID);
|
||||
} else {
|
||||
// get the file as a stream
|
||||
iS = activity.getResources().getAssets().open(fileName);
|
||||
}
|
||||
|
||||
ByteArrayOutputStream oS = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[iS.available()];
|
||||
int bytesRead = 0;
|
||||
while ((bytesRead = iS.read(buffer)) > 0) {
|
||||
oS.write(buffer);
|
||||
}
|
||||
oS.close();
|
||||
iS.close();
|
||||
|
||||
// return the output stream as a String
|
||||
return oS.toString();
|
||||
}
|
||||
|
||||
public static void saveFileContents(String dBFile, String machinesToExport) {
|
||||
// TODO Auto-generated method stub
|
||||
byteArrayToFile(machinesToExport.getBytes(), new File(dBFile));
|
||||
}
|
||||
|
||||
public static void byteArrayToFile(byte[] byteData, File filePath) {
|
||||
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream(filePath);
|
||||
fos.write(byteData);
|
||||
fos.close();
|
||||
|
||||
} catch (FileNotFoundException ex) {
|
||||
System.out.println("FileNotFoundException : " + ex);
|
||||
} catch (IOException ioe) {
|
||||
System.out.println("IOException : " + ioe);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static String getDataDir() {
|
||||
|
||||
String dataDir = MainActivity.activity.getApplicationInfo().dataDir;
|
||||
PackageManager m = MainActivity.activity.getPackageManager();
|
||||
String packageName = MainActivity.activity.getPackageName();
|
||||
Log.v("VMExecutor", "Found packageName: " + packageName);
|
||||
|
||||
if (dataDir == null) {
|
||||
dataDir = "/data/data/" + packageName;
|
||||
}
|
||||
return dataDir;
|
||||
}
|
||||
|
||||
public static boolean fileValid(Context context, String path) {
|
||||
|
||||
if (path == null || path.equals(""))
|
||||
return true;
|
||||
if (path.startsWith("content://") || path.startsWith("/content/")) {
|
||||
int fd = get_fd(context, path);
|
||||
if (fd <= 0)
|
||||
return false;
|
||||
} else {
|
||||
File file = new File(path);
|
||||
return file.exists();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static HashMap<Integer, ParcelFileDescriptor> fds = new HashMap<Integer, ParcelFileDescriptor>();
|
||||
|
||||
public static int get_fd(final Context context, String path) {
|
||||
int fd = 0;
|
||||
if (path == null)
|
||||
return 0;
|
||||
|
||||
if (path.startsWith("/content") || path.startsWith("content://")) {
|
||||
path = path.replaceFirst("/content", "content:");
|
||||
|
||||
try {
|
||||
ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(Uri.parse(path), "rw");
|
||||
fd = pfd.getFd();
|
||||
fds.put(fd, pfd);
|
||||
} catch (final FileNotFoundException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(context, "Error: " + e, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
File file = new File(path);
|
||||
if (!file.exists())
|
||||
file.createNewFile();
|
||||
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_WRITE_ONLY);
|
||||
fd = pfd.getFd();
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
public static int close_fd(int fd) {
|
||||
|
||||
if (FileUtils.fds.containsKey(fd)) {
|
||||
ParcelFileDescriptor pfd = FileUtils.fds.get(fd);
|
||||
try {
|
||||
pfd.close();
|
||||
FileUtils.fds.remove(fd);
|
||||
return 0; // success for Native side
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static void writeToFile(String data, File file, Context context) {
|
||||
try {
|
||||
FileOutputStream fileOutStream = new FileOutputStream(file);
|
||||
OutputStreamWriter outputWriter = new OutputStreamWriter(fileOutStream);
|
||||
outputWriter.write(data);
|
||||
outputWriter.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static String readFromFile(Context context, File file) {
|
||||
String contents = null;
|
||||
try {
|
||||
int length = (int) file.length();
|
||||
|
||||
byte[] bytes = new byte[length];
|
||||
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
try {
|
||||
in.read(bytes);
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
|
||||
contents = new String(bytes);
|
||||
} catch (Exception e) {
|
||||
UIUtils.toastLong(context, e.toString());
|
||||
return "error";
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
public static boolean moveFile(String oldfilename, String newFolderPath, String newFilename) {
|
||||
File folder = new File(newFolderPath);
|
||||
if (!folder.exists())
|
||||
folder.mkdirs();
|
||||
|
||||
File oldfile = new File(oldfilename);
|
||||
File newFile = new File(newFolderPath, newFilename);
|
||||
|
||||
if (!newFile.exists())
|
||||
try {
|
||||
newFile.createNewFile();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
return oldfile.renameTo(newFile);
|
||||
}
|
||||
|
||||
}
|
||||
378
app/src/main/java/com/vectras/vm/utils/UIUtils.java
Normal file
378
app/src/main/java/com/vectras/vm/utils/UIUtils.java
Normal file
|
|
@ -0,0 +1,378 @@
|
|||
package com.vectras.vm.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Point;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.InputType;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.webkit.WebView;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import com.vectras.qemu.Config;
|
||||
import com.vectras.qemu.MainActivityCommon;
|
||||
import com.vectras.qemu.MainSettingsManager;
|
||||
import com.vectras.qemu.utils.FileUtils;
|
||||
import com.vectras.vm.R;
|
||||
import com.vectras.vm.AppConfig;
|
||||
import com.vectras.vm.logger.VectrasStatus;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Scanner;
|
||||
|
||||
public class UIUtils {
|
||||
|
||||
private static final String TAG = "UIUtils";
|
||||
|
||||
public static Spannable formatAndroidLog(String contents) {
|
||||
|
||||
Scanner scanner = null;
|
||||
Spannable formattedString = new SpannableString(contents);
|
||||
if(contents.length()==0)
|
||||
return formattedString;
|
||||
|
||||
try {
|
||||
scanner = new Scanner(contents);
|
||||
int counter = 0;
|
||||
ForegroundColorSpan colorSpan = null;
|
||||
while (scanner.hasNextLine()) {
|
||||
String line = scanner.nextLine();
|
||||
//FIXME: some devices don't have standard format for the log
|
||||
if (line.startsWith("E/") || line.contains(" E ")) {
|
||||
colorSpan = new ForegroundColorSpan(Color.rgb(255, 22, 22));
|
||||
} else if (line.startsWith("W/") || line.contains(" W ")) {
|
||||
colorSpan = new ForegroundColorSpan(Color.rgb(22, 44, 255));
|
||||
} else {
|
||||
colorSpan = null;
|
||||
}
|
||||
if (colorSpan!= null) {
|
||||
formattedString.setSpan(colorSpan, counter, counter + line.length(),
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
counter += line.length()+1;
|
||||
}
|
||||
|
||||
}catch (Exception ex) {
|
||||
Log.e(TAG, "Could not format vectras log: " + ex.getMessage());
|
||||
} finally {
|
||||
if(scanner!=null) {
|
||||
try {
|
||||
scanner.close();
|
||||
} catch (Exception ex) {
|
||||
if(Config.debug)
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return formattedString;
|
||||
}
|
||||
public static void toastLong(final Context activity, final String errStr) {
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
Toast toast = Toast.makeText(activity, errStr, Toast.LENGTH_LONG);
|
||||
toast.show();
|
||||
VectrasStatus.logInfo("<font color='yellow'>[I] "+errStr+"</font>");
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public static void showFileNotSupported(Activity context){
|
||||
UIAlert(context, "Error", "File path is not supported. Make sure you choose a file/directory from your internal storage or external sd card. Root and Download Directories are not supported.");
|
||||
}
|
||||
|
||||
|
||||
public static boolean onKeyboard(Activity activity, boolean toggle, View view) {
|
||||
// Prevent crashes from activating mouse when machine is paused
|
||||
if (MainActivityCommon.vmexecutor.paused == 1)
|
||||
return !toggle;
|
||||
|
||||
InputMethodManager inputMgr = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
|
||||
//XXX: we need to get the focused view to make this always work
|
||||
//inputMgr.toggleSoftInput(0, 0);
|
||||
|
||||
|
||||
// View view = activity.getCurrentFocus();
|
||||
if (toggle || !Config.enableToggleKeyboard){
|
||||
if(view!=null) {
|
||||
view.requestFocus();
|
||||
inputMgr.showSoftInput(view, InputMethodManager.SHOW_FORCED);
|
||||
}
|
||||
} else {
|
||||
if (view != null) {
|
||||
inputMgr.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
return !toggle;
|
||||
}
|
||||
|
||||
public static void hideKeyboard(Activity activity, View view) {
|
||||
InputMethodManager inputMgr = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
if (view != null) {
|
||||
inputMgr.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void toastShortTop(final Activity activity, final String errStr) {
|
||||
UIUtils.toast(activity, errStr, Gravity.TOP | Gravity.CENTER, Toast.LENGTH_SHORT);
|
||||
}
|
||||
|
||||
public static void toast(final Context context, final String errStr, final int gravity, final int length) {
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if(context instanceof Activity && ((Activity) context).isFinishing()) {
|
||||
return ;
|
||||
}
|
||||
Toast toast = Toast.makeText(context, errStr, length);
|
||||
toast.setGravity(gravity, 0, 0);
|
||||
toast.show();
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public static void toastShort(final Context context, final String errStr) {
|
||||
toast(context, errStr, Gravity.CENTER | Gravity.CENTER, Toast.LENGTH_SHORT);
|
||||
|
||||
}
|
||||
|
||||
public static void setOrientation(Activity activity) {
|
||||
int orientation = MainSettingsManager.getOrientationSetting(activity);
|
||||
switch (orientation) {
|
||||
case 0:
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
||||
break;
|
||||
case 1:
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
||||
break;
|
||||
case 2:
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
|
||||
break;
|
||||
case 3:
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
break;
|
||||
case 4:
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void onChangeLog(Activity activity) {
|
||||
PackageInfo pInfo = null;
|
||||
|
||||
try {
|
||||
pInfo = activity.getPackageManager().getPackageInfo(activity.getClass().getPackage().getName(),
|
||||
PackageManager.GET_META_DATA);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
com.vectras.qemu.utils.FileUtils fileutils = new com.vectras.qemu.utils.FileUtils();
|
||||
try {
|
||||
UIUtils.UIAlert(activity,"CHANGELOG", fileutils.LoadFile(activity, "CHANGELOG", false),
|
||||
0, false, "OK", null, null, null, null, null);
|
||||
} catch (IOException e) {
|
||||
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void showHints(Activity activity) {
|
||||
|
||||
|
||||
UIUtils.toastShortTop(activity, "Press Volume Down for Right Click");
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static boolean isLandscapeOrientation(Activity activity)
|
||||
{
|
||||
Display display = activity.getWindowManager().getDefaultDisplay();
|
||||
Point screenSize = new Point();
|
||||
display.getSize(screenSize);
|
||||
if(screenSize.x < screenSize.y)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void openURL(Activity activity, String url) {
|
||||
try {
|
||||
Intent fileIntent = new Intent(Intent.ACTION_VIEW);
|
||||
fileIntent.setData(Uri.parse(url));
|
||||
activity.startActivity(fileIntent);
|
||||
}catch (Exception ex) {
|
||||
UIUtils.toastShort(activity, "Could not open url");
|
||||
}
|
||||
}
|
||||
|
||||
public static void UIAlert(Activity activity, String title, String body) {
|
||||
|
||||
AlertDialog alertDialog;
|
||||
alertDialog = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create();
|
||||
alertDialog.setTitle(title);
|
||||
TextView textView = new TextView(activity);
|
||||
textView.setPadding(20,20,20,20);
|
||||
textView.setText(body);
|
||||
ScrollView view = new ScrollView(activity);
|
||||
view.addView(textView);
|
||||
alertDialog.setView(view);
|
||||
alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
alertDialog.show();
|
||||
}
|
||||
|
||||
public static void UIAlert(Activity activity, String title, String body, int textSize, boolean cancelable,
|
||||
String button1title, DialogInterface.OnClickListener button1Listener,
|
||||
String button2title, DialogInterface.OnClickListener button2Listener,
|
||||
String button3title, DialogInterface.OnClickListener button3Listener
|
||||
) {
|
||||
|
||||
AlertDialog alertDialog;
|
||||
alertDialog = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create();
|
||||
alertDialog.setTitle(title);
|
||||
alertDialog.setCanceledOnTouchOutside(cancelable);
|
||||
TextView textView = new TextView(activity);
|
||||
textView.setPadding(20,20,20,20);
|
||||
textView.setText(body);
|
||||
if(textSize>0)
|
||||
textView.setTextSize(textSize);
|
||||
textView.setBackgroundColor(Color.WHITE);
|
||||
textView.setTextColor(Color.BLACK);
|
||||
ScrollView view = new ScrollView(activity);
|
||||
view.addView(textView);
|
||||
alertDialog.setView(view);
|
||||
if(button1title!=null)
|
||||
alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, button1title, button1Listener);
|
||||
if(button2title!=null)
|
||||
alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, button2title, button2Listener);
|
||||
if(button3title!=null)
|
||||
alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, button3title, button3Listener);
|
||||
alertDialog.show();
|
||||
}
|
||||
|
||||
public static void UIAlertLog(final Activity activity, String title, Spannable body) {
|
||||
|
||||
AlertDialog alertDialog;
|
||||
alertDialog = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create();
|
||||
alertDialog.setTitle(title);
|
||||
TextView textView = new TextView(activity);
|
||||
textView.setPadding(20,20,20,20);
|
||||
textView.setText(body);
|
||||
textView.setBackgroundColor(Color.BLACK);
|
||||
textView.setTextSize(12);
|
||||
textView.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||
textView.setSingleLine(false);
|
||||
ScrollView view = new ScrollView(activity);
|
||||
view.addView(textView);
|
||||
alertDialog.setView(view);
|
||||
alertDialog.setCanceledOnTouchOutside(false);
|
||||
alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
alertDialog.setButton(DialogInterface.BUTTON_NEUTRAL, "Copy To", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
ClipboardManager clipboardManager = (ClipboardManager)
|
||||
activity.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clipData = ClipData.newPlainText("nonsense_data",
|
||||
body);
|
||||
clipboardManager.setPrimaryClip(clipData);
|
||||
UIUtils.toastShort(activity, "Copied to clipboard successfully!");
|
||||
return;
|
||||
}
|
||||
});
|
||||
alertDialog.show();
|
||||
}
|
||||
|
||||
public static void UIAlertHtml(String title, String body, Activity activity) {
|
||||
|
||||
AlertDialog alertDialog;
|
||||
alertDialog = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create();
|
||||
alertDialog.setTitle(title);
|
||||
|
||||
try {
|
||||
WebView webview = new WebView(activity);
|
||||
webview.loadData(body, "text/html", "UTF-8");
|
||||
alertDialog.setView(webview);
|
||||
} catch (Exception ex) {
|
||||
TextView textView = new TextView(activity);
|
||||
textView.setText(body);
|
||||
alertDialog.setView(textView);
|
||||
}
|
||||
|
||||
alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
alertDialog.show();
|
||||
}
|
||||
|
||||
public static void promptShowLog(final Activity activity) {
|
||||
|
||||
final AlertDialog alertDialog;
|
||||
alertDialog = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create();
|
||||
alertDialog.setTitle("Show log");
|
||||
TextView stateView = new TextView(activity);
|
||||
stateView.setText("Something happened during last run, do you want to see the log?");
|
||||
stateView.setPadding(20, 20, 20, 20);
|
||||
alertDialog.setView(stateView);
|
||||
|
||||
// alertDialog.setMessage(body);
|
||||
alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Yes",
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
|
||||
FileUtils.viewVectrasLog(activity);
|
||||
}
|
||||
});
|
||||
alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel",
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
alertDialog.show();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
873
app/src/main/java/com/vectras/vm/widgets/JoystickView.java
Normal file
873
app/src/main/java/com/vectras/vm/widgets/JoystickView.java
Normal file
|
|
@ -0,0 +1,873 @@
|
|||
package com.vectras.vm.widgets;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import com.vectras.vm.R;
|
||||
|
||||
public class JoystickView extends View
|
||||
implements
|
||||
Runnable {
|
||||
|
||||
|
||||
/*
|
||||
INTERFACES
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when a
|
||||
* JoystickView's button is moved
|
||||
*/
|
||||
public interface OnMoveListener {
|
||||
|
||||
/**
|
||||
* Called when a JoystickView's button has been moved
|
||||
* @param angle current angle
|
||||
* @param strength current strength
|
||||
*/
|
||||
void onMove(int angle, int strength);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when a JoystickView
|
||||
* is touched and held by multiple pointers.
|
||||
*/
|
||||
public interface OnMultipleLongPressListener {
|
||||
/**
|
||||
* Called when a JoystickView has been touch and held enough time by multiple pointers.
|
||||
*/
|
||||
void onMultipleLongPress();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
CONSTANTS
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default refresh rate as a time in milliseconds to send move values through callback
|
||||
*/
|
||||
private static final int DEFAULT_LOOP_INTERVAL = 50; // in milliseconds
|
||||
|
||||
/**
|
||||
* Used to allow a slight move without cancelling MultipleLongPress
|
||||
*/
|
||||
private static final int MOVE_TOLERANCE = 10;
|
||||
|
||||
/**
|
||||
* Default color for button
|
||||
*/
|
||||
private static final int DEFAULT_COLOR_BUTTON = Color.BLACK;
|
||||
|
||||
/**
|
||||
* Default color for border
|
||||
*/
|
||||
private static final int DEFAULT_COLOR_BORDER = Color.TRANSPARENT;
|
||||
|
||||
/**
|
||||
* Default alpha for border
|
||||
*/
|
||||
private static final int DEFAULT_ALPHA_BORDER = 255;
|
||||
|
||||
/**
|
||||
* Default background color
|
||||
*/
|
||||
private static final int DEFAULT_BACKGROUND_COLOR = Color.TRANSPARENT;
|
||||
|
||||
/**
|
||||
* Default View's size
|
||||
*/
|
||||
private static final int DEFAULT_SIZE = 200;
|
||||
|
||||
/**
|
||||
* Default border's width
|
||||
*/
|
||||
private static final int DEFAULT_WIDTH_BORDER = 3;
|
||||
|
||||
/**
|
||||
* Default behavior to fixed center (not auto-defined)
|
||||
*/
|
||||
private static final boolean DEFAULT_FIXED_CENTER = true;
|
||||
|
||||
|
||||
/**
|
||||
* Default behavior to auto re-center button (automatically recenter the button)
|
||||
*/
|
||||
private static final boolean DEFAULT_AUTO_RECENTER_BUTTON = true;
|
||||
|
||||
|
||||
/**
|
||||
* Default behavior to button stickToBorder (button stay on the border)
|
||||
*/
|
||||
private static final boolean DEFAULT_BUTTON_STICK_TO_BORDER = false;
|
||||
|
||||
|
||||
// DRAWING
|
||||
private Paint mPaintCircleButton;
|
||||
private Paint mPaintCircleBorder;
|
||||
private Paint mPaintBackground;
|
||||
|
||||
private Paint mPaintBitmapButton;
|
||||
private Bitmap mButtonBitmap;
|
||||
|
||||
|
||||
/**
|
||||
* Ratio use to define the size of the button
|
||||
*/
|
||||
private float mButtonSizeRatio;
|
||||
|
||||
|
||||
/**
|
||||
* Ratio use to define the size of the background
|
||||
*
|
||||
*/
|
||||
private float mBackgroundSizeRatio;
|
||||
|
||||
|
||||
// COORDINATE
|
||||
private int mPosX = 0;
|
||||
private int mPosY = 0;
|
||||
private int mCenterX = 0;
|
||||
private int mCenterY = 0;
|
||||
|
||||
private int mFixedCenterX = 0;
|
||||
private int mFixedCenterY = 0;
|
||||
|
||||
/**
|
||||
* Used to adapt behavior whether it is auto-defined center (false) or fixed center (true)
|
||||
*/
|
||||
private boolean mFixedCenter;
|
||||
|
||||
|
||||
/**
|
||||
* Used to adapt behavior whether the button is automatically re-centered (true)
|
||||
* when released or not (false)
|
||||
*/
|
||||
private boolean mAutoReCenterButton;
|
||||
|
||||
|
||||
/**
|
||||
* Used to adapt behavior whether the button is stick to border (true) or
|
||||
* could be anywhere (when false - similar to regular behavior)
|
||||
*/
|
||||
private boolean mButtonStickToBorder;
|
||||
|
||||
|
||||
/**
|
||||
* Used to enabled/disabled the Joystick. When disabled (enabled to false) the joystick button
|
||||
* can't move and onMove is not called.
|
||||
*/
|
||||
private boolean mEnabled;
|
||||
|
||||
|
||||
// SIZE
|
||||
private int mButtonRadius;
|
||||
private int mBorderRadius;
|
||||
|
||||
|
||||
/**
|
||||
* Alpha of the border (to use when changing color dynamically)
|
||||
*/
|
||||
private int mBorderAlpha;
|
||||
|
||||
|
||||
/**
|
||||
* Based on mBorderRadius but a bit smaller (minus half the stroke size of the border)
|
||||
*/
|
||||
private float mBackgroundRadius;
|
||||
|
||||
|
||||
/**
|
||||
* Listener used to dispatch OnMove event
|
||||
*/
|
||||
private OnMoveListener mCallback;
|
||||
|
||||
private long mLoopInterval = DEFAULT_LOOP_INTERVAL;
|
||||
private Thread mThread = new Thread(this);
|
||||
|
||||
|
||||
/**
|
||||
* Listener used to dispatch MultipleLongPress event
|
||||
*/
|
||||
private OnMultipleLongPressListener mOnMultipleLongPressListener;
|
||||
|
||||
private final Handler mHandlerMultipleLongPress = new Handler();
|
||||
private Runnable mRunnableMultipleLongPress;
|
||||
private int mMoveTolerance;
|
||||
|
||||
|
||||
/**
|
||||
* Default value.
|
||||
* Both direction correspond to horizontal and vertical movement
|
||||
*/
|
||||
public static int BUTTON_DIRECTION_BOTH = 0;
|
||||
|
||||
/**
|
||||
* The allowed direction of the button is define by the value of this parameter:
|
||||
* - a negative value for horizontal axe
|
||||
* - a positive value for vertical axe
|
||||
* - zero for both axes
|
||||
*/
|
||||
private int mButtonDirection = 0;
|
||||
|
||||
|
||||
/*
|
||||
CONSTRUCTORS
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Simple constructor to use when creating a JoystickView from code.
|
||||
* Call another constructor passing null to Attribute.
|
||||
* @param context The Context the JoystickView is running in, through which it can
|
||||
* access the current theme, resources, etc.
|
||||
*/
|
||||
public JoystickView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
|
||||
public JoystickView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
this(context, attrs);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor that is called when inflating a JoystickView from XML. This is called
|
||||
* when a JoystickView is being constructed from an XML file, supplying attributes
|
||||
* that were specified in the XML file.
|
||||
* @param context The Context the JoystickView is running in, through which it can
|
||||
* access the current theme, resources, etc.
|
||||
* @param attrs The attributes of the XML tag that is inflating the JoystickView.
|
||||
*/
|
||||
public JoystickView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
TypedArray styledAttributes = context.getTheme().obtainStyledAttributes(
|
||||
attrs,
|
||||
R.styleable.JoystickView,
|
||||
0, 0
|
||||
);
|
||||
|
||||
int buttonColor;
|
||||
int borderColor;
|
||||
int backgroundColor;
|
||||
int borderWidth;
|
||||
Drawable buttonDrawable;
|
||||
try {
|
||||
buttonColor = styledAttributes.getColor(R.styleable.JoystickView_JV_buttonColor, DEFAULT_COLOR_BUTTON);
|
||||
borderColor = styledAttributes.getColor(R.styleable.JoystickView_JV_borderColor, DEFAULT_COLOR_BORDER);
|
||||
mBorderAlpha = styledAttributes.getInt(R.styleable.JoystickView_JV_borderAlpha, DEFAULT_ALPHA_BORDER);
|
||||
backgroundColor = styledAttributes.getColor(R.styleable.JoystickView_JV_backgroundColor, DEFAULT_BACKGROUND_COLOR);
|
||||
borderWidth = styledAttributes.getDimensionPixelSize(R.styleable.JoystickView_JV_borderWidth, DEFAULT_WIDTH_BORDER);
|
||||
mFixedCenter = styledAttributes.getBoolean(R.styleable.JoystickView_JV_fixedCenter, DEFAULT_FIXED_CENTER);
|
||||
mAutoReCenterButton = styledAttributes.getBoolean(R.styleable.JoystickView_JV_autoReCenterButton, DEFAULT_AUTO_RECENTER_BUTTON);
|
||||
mButtonStickToBorder = styledAttributes.getBoolean(R.styleable.JoystickView_JV_buttonStickToBorder, DEFAULT_BUTTON_STICK_TO_BORDER);
|
||||
buttonDrawable = styledAttributes.getDrawable(R.styleable.JoystickView_JV_buttonImage);
|
||||
mEnabled = styledAttributes.getBoolean(R.styleable.JoystickView_JV_enabled, true);
|
||||
mButtonSizeRatio = styledAttributes.getFraction(R.styleable.JoystickView_JV_buttonSizeRatio, 1, 1, 0.25f);
|
||||
mBackgroundSizeRatio = styledAttributes.getFraction(R.styleable.JoystickView_JV_backgroundSizeRatio, 1, 1, 0.75f);
|
||||
mButtonDirection = styledAttributes.getInteger(R.styleable.JoystickView_JV_buttonDirection, BUTTON_DIRECTION_BOTH);
|
||||
} finally {
|
||||
styledAttributes.recycle();
|
||||
}
|
||||
|
||||
// Initialize the drawing according to attributes
|
||||
|
||||
mPaintCircleButton = new Paint();
|
||||
mPaintCircleButton.setAntiAlias(true);
|
||||
mPaintCircleButton.setColor(buttonColor);
|
||||
mPaintCircleButton.setStyle(Paint.Style.FILL);
|
||||
|
||||
if (buttonDrawable != null) {
|
||||
if (buttonDrawable instanceof BitmapDrawable) {
|
||||
mButtonBitmap = ((BitmapDrawable) buttonDrawable).getBitmap();
|
||||
mPaintBitmapButton = new Paint();
|
||||
}
|
||||
}
|
||||
|
||||
mPaintCircleBorder = new Paint();
|
||||
mPaintCircleBorder.setAntiAlias(true);
|
||||
mPaintCircleBorder.setColor(borderColor);
|
||||
mPaintCircleBorder.setStyle(Paint.Style.STROKE);
|
||||
mPaintCircleBorder.setStrokeWidth(borderWidth);
|
||||
|
||||
if (borderColor != Color.TRANSPARENT) {
|
||||
mPaintCircleBorder.setAlpha(mBorderAlpha);
|
||||
}
|
||||
|
||||
mPaintBackground = new Paint();
|
||||
mPaintBackground.setAntiAlias(true);
|
||||
mPaintBackground.setColor(backgroundColor);
|
||||
mPaintBackground.setStyle(Paint.Style.FILL);
|
||||
|
||||
|
||||
// Init Runnable for MultiLongPress
|
||||
|
||||
mRunnableMultipleLongPress = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mOnMultipleLongPressListener != null)
|
||||
mOnMultipleLongPressListener.onMultipleLongPress();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private void initPosition() {
|
||||
// get the center of view to position circle
|
||||
mFixedCenterX = mCenterX = mPosX = getWidth() / 2;
|
||||
mFixedCenterY = mCenterY = mPosY = getWidth() / 2;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Draw the background, the border and the button
|
||||
* @param canvas the canvas on which the shapes will be drawn
|
||||
*/
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
// Draw the background
|
||||
canvas.drawCircle(mFixedCenterX, mFixedCenterY, mBackgroundRadius, mPaintBackground);
|
||||
|
||||
// Draw the circle border
|
||||
canvas.drawCircle(mFixedCenterX, mFixedCenterY, mBorderRadius, mPaintCircleBorder);
|
||||
|
||||
// Draw the button from image
|
||||
if (mButtonBitmap != null) {
|
||||
canvas.drawBitmap(
|
||||
mButtonBitmap,
|
||||
mPosX + mFixedCenterX - mCenterX - mButtonRadius,
|
||||
mPosY + mFixedCenterY - mCenterY - mButtonRadius,
|
||||
mPaintBitmapButton
|
||||
);
|
||||
}
|
||||
// Draw the button as simple circle
|
||||
else {
|
||||
canvas.drawCircle(
|
||||
mPosX + mFixedCenterX - mCenterX,
|
||||
mPosY + mFixedCenterY - mCenterY,
|
||||
mButtonRadius,
|
||||
mPaintCircleButton
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is called during layout when the size of this view has changed.
|
||||
* Here we get the center of the view and the radius to draw all the shapes.
|
||||
*
|
||||
* @param w Current width of this view.
|
||||
* @param h Current height of this view.
|
||||
* @param oldW Old width of this view.
|
||||
* @param oldH Old height of this view.
|
||||
*/
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldW, int oldH) {
|
||||
super.onSizeChanged(w, h, oldW, oldH);
|
||||
|
||||
initPosition();
|
||||
|
||||
// radius based on smallest size : height OR width
|
||||
int d = Math.min(w, h);
|
||||
mButtonRadius = (int) (d / 2 * mButtonSizeRatio);
|
||||
mBorderRadius = (int) (d / 2 * mBackgroundSizeRatio);
|
||||
mBackgroundRadius = mBorderRadius - (mPaintCircleBorder.getStrokeWidth() / 2);
|
||||
|
||||
if (mButtonBitmap != null)
|
||||
mButtonBitmap = Bitmap.createScaledBitmap(mButtonBitmap, mButtonRadius * 2, mButtonRadius * 2, true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
// setting the measured values to resize the view to a certain width and height
|
||||
int d = Math.min(measure(widthMeasureSpec), measure(heightMeasureSpec));
|
||||
setMeasuredDimension(d, d);
|
||||
}
|
||||
|
||||
|
||||
private int measure(int measureSpec) {
|
||||
if (MeasureSpec.getMode(measureSpec) == MeasureSpec.UNSPECIFIED) {
|
||||
// if no bounds are specified return a default size (200)
|
||||
return DEFAULT_SIZE;
|
||||
} else {
|
||||
// As you want to fill the available space
|
||||
// always return the full available bounds.
|
||||
return MeasureSpec.getSize(measureSpec);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
USER EVENT
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Handle touch screen motion event. Move the button according to the
|
||||
* finger coordinate and detect longPress by multiple pointers only.
|
||||
*
|
||||
* @param event The motion event.
|
||||
* @return True if the event was handled, false otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
// if disabled we don't move the
|
||||
if (!mEnabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// to move the button according to the finger coordinate
|
||||
// (or limited to one axe according to direction option
|
||||
mPosY = mButtonDirection < 0 ? mCenterY : (int) event.getY(); // direction negative is horizontal axe
|
||||
mPosX = mButtonDirection > 0 ? mCenterX : (int) event.getX(); // direction positive is vertical axe
|
||||
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
|
||||
// stop listener because the finger left the touch screen
|
||||
mThread.interrupt();
|
||||
|
||||
// re-center the button or not (depending on settings)
|
||||
if (mAutoReCenterButton) {
|
||||
resetButtonPosition();
|
||||
|
||||
// update now the last strength and angle which should be zero after resetButton
|
||||
if (mCallback != null)
|
||||
mCallback.onMove(getAngle(), getStrength());
|
||||
}
|
||||
|
||||
// if mAutoReCenterButton is false we will send the last strength and angle a bit
|
||||
// later only after processing new position X and Y otherwise it could be above the border limit
|
||||
}
|
||||
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
if (mThread != null && mThread.isAlive()) {
|
||||
mThread.interrupt();
|
||||
}
|
||||
|
||||
mThread = new Thread(this);
|
||||
mThread.start();
|
||||
|
||||
if (mCallback != null)
|
||||
mCallback.onMove(getAngle(), getStrength());
|
||||
}
|
||||
|
||||
// handle first touch and long press with multiple touch only
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// when the first touch occurs we update the center (if set to auto-defined center)
|
||||
if (!mFixedCenter) {
|
||||
mCenterX = mPosX;
|
||||
mCenterY = mPosY;
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_DOWN: {
|
||||
// when the second finger touch
|
||||
if (event.getPointerCount() == 2) {
|
||||
mHandlerMultipleLongPress.postDelayed(mRunnableMultipleLongPress, ViewConfiguration.getLongPressTimeout()*2);
|
||||
mMoveTolerance = MOVE_TOLERANCE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
mMoveTolerance--;
|
||||
if (mMoveTolerance == 0) {
|
||||
mHandlerMultipleLongPress.removeCallbacks(mRunnableMultipleLongPress);
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_UP: {
|
||||
// when the last multiple touch is released
|
||||
if (event.getPointerCount() == 2) {
|
||||
mHandlerMultipleLongPress.removeCallbacks(mRunnableMultipleLongPress);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
double abs = Math.sqrt((mPosX - mCenterX) * (mPosX - mCenterX)
|
||||
+ (mPosY - mCenterY) * (mPosY - mCenterY));
|
||||
|
||||
// (abs > mBorderRadius) means button is too far therefore we limit to border
|
||||
// (buttonStickBorder && abs != 0) means wherever is the button we stick it to the border except when abs == 0
|
||||
if (abs > mBorderRadius || (mButtonStickToBorder && abs != 0)) {
|
||||
mPosX = (int) ((mPosX - mCenterX) * mBorderRadius / abs + mCenterX);
|
||||
mPosY = (int) ((mPosY - mCenterY) * mBorderRadius / abs + mCenterY);
|
||||
}
|
||||
|
||||
if (!mAutoReCenterButton) {
|
||||
// Now update the last strength and angle if not reset to center
|
||||
if (mCallback != null)
|
||||
mCallback.onMove(getAngle(), getStrength());
|
||||
}
|
||||
|
||||
|
||||
// to force a new draw
|
||||
invalidate();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
GETTERS
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Process the angle following the 360° counter-clock protractor rules.
|
||||
* @return the angle of the button
|
||||
*/
|
||||
private int getAngle() {
|
||||
int angle = (int) Math.toDegrees(Math.atan2(mCenterY - mPosY, mPosX - mCenterX));
|
||||
return angle < 0 ? angle + 360 : angle; // make it as a regular counter-clock protractor
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the strength as a percentage of the distance between the center and the border.
|
||||
* @return the strength of the button
|
||||
*/
|
||||
private int getStrength() {
|
||||
return (int) (100 * Math.sqrt((mPosX - mCenterX)
|
||||
* (mPosX - mCenterX) + (mPosY - mCenterY)
|
||||
* (mPosY - mCenterY)) / mBorderRadius);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reset the button position to the center.
|
||||
*/
|
||||
public void resetButtonPosition() {
|
||||
mPosX = mCenterX;
|
||||
mPosY = mCenterY;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the current direction allowed for the button to move
|
||||
* @return Actually return an integer corresponding to the direction:
|
||||
* - A negative value is horizontal axe,
|
||||
* - A positive value is vertical axe,
|
||||
* - Zero means both axes
|
||||
*/
|
||||
public int getButtonDirection() {
|
||||
return mButtonDirection;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the state of the joystick. False when the button don't move.
|
||||
* @return the state of the joystick
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return mEnabled;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the size of the button (as a ratio of the total width/height)
|
||||
* Default is 0.25 (25%).
|
||||
* @return button size (value between 0.0 and 1.0)
|
||||
*/
|
||||
public float getButtonSizeRatio() {
|
||||
return mButtonSizeRatio;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the size of the background (as a ratio of the total width/height)
|
||||
* Default is 0.75 (75%).
|
||||
* @return background size (value between 0.0 and 1.0)
|
||||
*/
|
||||
public float getmBackgroundSizeRatio() {
|
||||
return mBackgroundSizeRatio;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the current behavior of the auto re-center button
|
||||
* @return True if automatically re-centered or False if not
|
||||
*/
|
||||
public boolean isAutoReCenterButton() {
|
||||
return mAutoReCenterButton;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the current behavior of the button stick to border
|
||||
* @return True if the button stick to the border otherwise False
|
||||
*/
|
||||
public boolean isButtonStickToBorder() {
|
||||
return mButtonStickToBorder;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the relative X coordinate of button center related
|
||||
* to top-left virtual corner of the border
|
||||
* @return coordinate of X (normalized between 0 and 100)
|
||||
*/
|
||||
public int getNormalizedX() {
|
||||
if (getWidth() == 0) {
|
||||
return 50;
|
||||
}
|
||||
return Math.round((mPosX-mButtonRadius)*100.0f/(getWidth()-mButtonRadius*2));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the relative Y coordinate of the button center related
|
||||
* to top-left virtual corner of the border
|
||||
* @return coordinate of Y (normalized between 0 and 100)
|
||||
*/
|
||||
public int getNormalizedY() {
|
||||
if (getHeight() == 0) {
|
||||
return 50;
|
||||
}
|
||||
return Math.round((mPosY-mButtonRadius)*100.0f/(getHeight()-mButtonRadius*2));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the alpha of the border
|
||||
* @return it should be an integer between 0 and 255 previously set
|
||||
*/
|
||||
public int getBorderAlpha() {
|
||||
return mBorderAlpha;
|
||||
}
|
||||
|
||||
/*
|
||||
SETTERS
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Set an image to the button with a drawable
|
||||
* @param d drawable to pick the image
|
||||
*/
|
||||
public void setButtonDrawable(Drawable d) {
|
||||
if (d != null) {
|
||||
if (d instanceof BitmapDrawable) {
|
||||
mButtonBitmap = ((BitmapDrawable) d).getBitmap();
|
||||
|
||||
if (mButtonRadius != 0) {
|
||||
mButtonBitmap = Bitmap.createScaledBitmap(
|
||||
mButtonBitmap,
|
||||
mButtonRadius * 2,
|
||||
mButtonRadius * 2,
|
||||
true);
|
||||
}
|
||||
|
||||
if (mPaintBitmapButton != null)
|
||||
mPaintBitmapButton = new Paint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the button color for this JoystickView.
|
||||
* @param color the color of the button
|
||||
*/
|
||||
public void setButtonColor(int color) {
|
||||
mPaintCircleButton.setColor(color);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the border color for this JoystickView.
|
||||
* @param color the color of the border
|
||||
*/
|
||||
public void setBorderColor(int color) {
|
||||
mPaintCircleBorder.setColor(color);
|
||||
if (color != Color.TRANSPARENT) {
|
||||
mPaintCircleBorder.setAlpha(mBorderAlpha);
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the border alpha for this JoystickView.
|
||||
* @param alpha the transparency of the border between 0 and 255
|
||||
*/
|
||||
public void setBorderAlpha(int alpha) {
|
||||
mBorderAlpha = alpha;
|
||||
mPaintCircleBorder.setAlpha(alpha);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the background color for this JoystickView.
|
||||
* @param color the color of the background
|
||||
*/
|
||||
@Override
|
||||
public void setBackgroundColor(int color) {
|
||||
mPaintBackground.setColor(color);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the border width for this JoystickView.
|
||||
* @param width the width of the border
|
||||
*/
|
||||
public void setBorderWidth(int width) {
|
||||
mPaintCircleBorder.setStrokeWidth(width);
|
||||
mBackgroundRadius = mBorderRadius - (width / 2.0f);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register a callback to be invoked when this JoystickView's button is moved
|
||||
* @param l The callback that will run
|
||||
*/
|
||||
public void setOnMoveListener(OnMoveListener l) {
|
||||
setOnMoveListener(l, DEFAULT_LOOP_INTERVAL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register a callback to be invoked when this JoystickView's button is moved
|
||||
* @param l The callback that will run
|
||||
* @param loopInterval Refresh rate to be invoked in milliseconds
|
||||
*/
|
||||
public void setOnMoveListener(OnMoveListener l, int loopInterval) {
|
||||
mCallback = l;
|
||||
mLoopInterval = loopInterval;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register a callback to be invoked when this JoystickView is touch and held by multiple pointers
|
||||
* @param l The callback that will run
|
||||
*/
|
||||
public void setOnMultiLongPressListener(OnMultipleLongPressListener l) {
|
||||
mOnMultipleLongPressListener = l;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the joystick center's behavior (fixed or auto-defined)
|
||||
* @param fixedCenter True for fixed center, False for auto-defined center based on touch down
|
||||
*/
|
||||
public void setFixedCenter(boolean fixedCenter) {
|
||||
// if we set to "fixed" we make sure to re-init position related to the width of the joystick
|
||||
if (fixedCenter) {
|
||||
initPosition();
|
||||
}
|
||||
mFixedCenter = fixedCenter;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enable or disable the joystick
|
||||
* @param enabled False mean the button won't move and onMove won't be called
|
||||
*/
|
||||
public void setEnabled(boolean enabled) {
|
||||
mEnabled = enabled;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the joystick button size (as a fraction of the real width/height)
|
||||
* By default it is 25% (0.25).
|
||||
* @param newRatio between 0.0 and 1.0
|
||||
*/
|
||||
public void setButtonSizeRatio(float newRatio) {
|
||||
if (newRatio > 0.0f & newRatio <= 1.0f) {
|
||||
mButtonSizeRatio = newRatio;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the joystick button size (as a fraction of the real width/height)
|
||||
* By default it is 75% (0.75).
|
||||
* Not working if the background is an image.
|
||||
* @param newRatio between 0.0 and 1.0
|
||||
*/
|
||||
public void setBackgroundSizeRatio(float newRatio) {
|
||||
if (newRatio > 0.0f & newRatio <= 1.0f) {
|
||||
mBackgroundSizeRatio = newRatio;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the current behavior of the auto re-center button
|
||||
* @param b True if automatically re-centered or False if not
|
||||
*/
|
||||
public void setAutoReCenterButton(boolean b) {
|
||||
mAutoReCenterButton = b;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the current behavior of the button stick to border
|
||||
* @param b True if the button stick to the border or False (default) if not
|
||||
*/
|
||||
public void setButtonStickToBorder(boolean b) {
|
||||
mButtonStickToBorder = b;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the current authorized direction for the button to move
|
||||
* @param direction the value will define the authorized direction:
|
||||
* - any negative value (such as -1) for horizontal axe
|
||||
* - any positive value (such as 1) for vertical axe
|
||||
* - zero (0) for the full direction (both axes)
|
||||
*/
|
||||
public void setButtonDirection(int direction) {
|
||||
mButtonDirection = direction;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
IMPLEMENTS
|
||||
*/
|
||||
|
||||
|
||||
@Override // Runnable
|
||||
public void run() {
|
||||
while (!Thread.interrupted()) {
|
||||
post(new Runnable() {
|
||||
public void run() {
|
||||
if (mCallback != null)
|
||||
mCallback.onMove(getAngle(), getStrength());
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
Thread.sleep(mLoopInterval);
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
370
app/src/main/java/com/vectras/vm/widgets/RadioGroupPlus.java
Normal file
370
app/src/main/java/com/vectras/vm/widgets/RadioGroupPlus.java
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
package com.vectras.vm.widgets;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.RadioGroup;
|
||||
import androidx.annotation.IdRes;
|
||||
|
||||
public class RadioGroupPlus extends LinearLayout {
|
||||
// holds the checked id; the selection is empty by default
|
||||
private int mCheckedId = -1;
|
||||
// tracks children radio buttons checked state
|
||||
private CompoundButton.OnCheckedChangeListener mChildOnCheckedChangeListener;
|
||||
// when true, mOnCheckedChangeListener discards events
|
||||
private boolean mProtectFromCheckedChange = false;
|
||||
private OnCheckedChangeListener mOnCheckedChangeListener;
|
||||
private PassThroughHierarchyChangeListener mPassThroughListener;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public RadioGroupPlus(Context context) {
|
||||
super(context);
|
||||
setOrientation(VERTICAL);
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public RadioGroupPlus(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
// retrieve selected radio button as requested by the user in the
|
||||
// XML layout file
|
||||
//TODO: fix ignored attributes
|
||||
// TypedArray attributes = context.obtainStyledAttributes(
|
||||
// attrs, com.android.internal.R.styleable.RadioGroup, com.android.internal.R.attr.radioButtonStyle, 0);
|
||||
|
||||
// int value = attributes.getResourceId(com.android.internal.R.styleable.RadioGroup_checkedButton, View.NO_ID);
|
||||
// if (value != View.NO_ID) {
|
||||
// mCheckedId = value;
|
||||
// }
|
||||
|
||||
// final int index = attributes.getInt(com.android.internal.R.styleable.RadioGroup_orientation, VERTICAL);
|
||||
// setOrientation(index);
|
||||
|
||||
// attributes.recycle();
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
mChildOnCheckedChangeListener = new CheckedStateTracker();
|
||||
mPassThroughListener = new PassThroughHierarchyChangeListener();
|
||||
super.setOnHierarchyChangeListener(mPassThroughListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
|
||||
// the user listener is delegated to our pass-through listener
|
||||
mPassThroughListener.mOnHierarchyChangeListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
|
||||
// checks the appropriate radio button as requested in the XML file
|
||||
if (mCheckedId != -1) {
|
||||
mProtectFromCheckedChange = true;
|
||||
setCheckedStateForView(mCheckedId, true);
|
||||
mProtectFromCheckedChange = false;
|
||||
setCheckedId(mCheckedId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addView(View child, int index, ViewGroup.LayoutParams params) {
|
||||
if (child instanceof RadioButton) {
|
||||
final RadioButton button = (RadioButton) child;
|
||||
if (button.isChecked()) {
|
||||
mProtectFromCheckedChange = true;
|
||||
if (mCheckedId != -1) {
|
||||
setCheckedStateForView(mCheckedId, false);
|
||||
}
|
||||
mProtectFromCheckedChange = false;
|
||||
setCheckedId(button.getId());
|
||||
}
|
||||
}
|
||||
|
||||
super.addView(child, index, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the selection to the radio button whose identifier is passed in
|
||||
* parameter. Using -1 as the selection identifier clears the selection;
|
||||
* such an operation is equivalent to invoking {@link #clearCheck()}.</p>
|
||||
*
|
||||
* @param id the unique id of the radio button to select in this group
|
||||
* @see #getCheckedRadioButtonId()
|
||||
* @see #clearCheck()
|
||||
*/
|
||||
public void check(@IdRes int id) {
|
||||
// don't even bother
|
||||
if (id != -1 && (id == mCheckedId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCheckedId != -1) {
|
||||
setCheckedStateForView(mCheckedId, false);
|
||||
}
|
||||
|
||||
if (id != -1) {
|
||||
setCheckedStateForView(id, true);
|
||||
}
|
||||
|
||||
setCheckedId(id);
|
||||
}
|
||||
|
||||
private void setCheckedId(@IdRes int id) {
|
||||
mCheckedId = id;
|
||||
if (mOnCheckedChangeListener != null) {
|
||||
mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
|
||||
}
|
||||
}
|
||||
|
||||
private void setCheckedStateForView(int viewId, boolean checked) {
|
||||
View checkedView = findViewById(viewId);
|
||||
if (checkedView != null && checkedView instanceof RadioButton) {
|
||||
((RadioButton) checkedView).setChecked(checked);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns the identifier of the selected radio button in this group.
|
||||
* Upon empty selection, the returned value is -1.</p>
|
||||
*
|
||||
* @return the unique id of the selected radio button in this group
|
||||
* @attr ref android.R.styleable#RadioGroup_checkedButton
|
||||
* @see #check(int)
|
||||
* @see #clearCheck()
|
||||
*/
|
||||
@IdRes
|
||||
public int getCheckedRadioButtonId() {
|
||||
return mCheckedId;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Clears the selection. When the selection is cleared, no radio button
|
||||
* in this group is selected and {@link #getCheckedRadioButtonId()} returns
|
||||
* null.</p>
|
||||
*
|
||||
* @see #check(int)
|
||||
* @see #getCheckedRadioButtonId()
|
||||
*/
|
||||
public void clearCheck() {
|
||||
check(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Register a callback to be invoked when the checked radio button
|
||||
* changes in this group.</p>
|
||||
*
|
||||
* @param listener the callback to call on checked state change
|
||||
*/
|
||||
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
|
||||
mOnCheckedChangeListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public LayoutParams generateLayoutParams(AttributeSet attrs) {
|
||||
return new RadioGroupPlus.LayoutParams(getContext(), attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
|
||||
return p instanceof RadioGroup.LayoutParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LinearLayout.LayoutParams generateDefaultLayoutParams() {
|
||||
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getAccessibilityClassName() {
|
||||
return RadioGroup.class.getName();
|
||||
}
|
||||
|
||||
public static class LayoutParams extends LinearLayout.LayoutParams {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public LayoutParams(Context c, AttributeSet attrs) {
|
||||
super(c, attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public LayoutParams(int w, int h) {
|
||||
super(w, h);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public LayoutParams(int w, int h, float initWeight) {
|
||||
super(w, h, initWeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public LayoutParams(ViewGroup.LayoutParams p) {
|
||||
super(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public LayoutParams(MarginLayoutParams source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Fixes the child's width to
|
||||
* {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and the child's
|
||||
* height to {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
|
||||
* when not specified in the XML file.</p>
|
||||
*
|
||||
* @param a the styled attributes set
|
||||
* @param widthAttr the width attribute to fetch
|
||||
* @param heightAttr the height attribute to fetch
|
||||
*/
|
||||
@Override
|
||||
protected void setBaseAttributes(TypedArray a,
|
||||
int widthAttr, int heightAttr) {
|
||||
|
||||
if (a.hasValue(widthAttr)) {
|
||||
width = a.getLayoutDimension(widthAttr, "layout_width");
|
||||
} else {
|
||||
width = WRAP_CONTENT;
|
||||
}
|
||||
|
||||
if (a.hasValue(heightAttr)) {
|
||||
height = a.getLayoutDimension(heightAttr, "layout_height");
|
||||
} else {
|
||||
height = WRAP_CONTENT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Interface definition for a callback to be invoked when the checked
|
||||
* radio button changed in this group.</p>
|
||||
*/
|
||||
public interface OnCheckedChangeListener {
|
||||
/**
|
||||
* <p>Called when the checked radio button has changed. When the
|
||||
* selection is cleared, checkedId is -1.</p>
|
||||
*
|
||||
* @param group the group in which the checked radio button has changed
|
||||
* @param checkedId the unique identifier of the newly checked radio button
|
||||
*/
|
||||
public void onCheckedChanged(RadioGroupPlus group, @IdRes int checkedId);
|
||||
}
|
||||
|
||||
private class CheckedStateTracker implements CompoundButton.OnCheckedChangeListener {
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
// prevents from infinite recursion
|
||||
if (mProtectFromCheckedChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
mProtectFromCheckedChange = true;
|
||||
if (mCheckedId != -1) {
|
||||
setCheckedStateForView(mCheckedId, false);
|
||||
}
|
||||
mProtectFromCheckedChange = false;
|
||||
|
||||
int id = buttonView.getId();
|
||||
setCheckedId(id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>A pass-through listener acts upon the events and dispatches them
|
||||
* to another listener. This allows the table layout to set its own internal
|
||||
* hierarchy change listener without preventing the user to setup his.</p>
|
||||
*/
|
||||
private class PassThroughHierarchyChangeListener implements
|
||||
ViewGroup.OnHierarchyChangeListener {
|
||||
private ViewGroup.OnHierarchyChangeListener mOnHierarchyChangeListener;
|
||||
|
||||
public void traverseTree(View view) {
|
||||
if (view instanceof RadioButton) {
|
||||
int id = view.getId();
|
||||
// generates an id if it's missing
|
||||
if (id == View.NO_ID) {
|
||||
id = View.generateViewId();
|
||||
view.setId(id);
|
||||
}
|
||||
((RadioButton) view).setOnCheckedChangeListener(
|
||||
mChildOnCheckedChangeListener);
|
||||
}
|
||||
if (!(view instanceof ViewGroup)) {
|
||||
return;
|
||||
}
|
||||
ViewGroup viewGroup = (ViewGroup) view;
|
||||
if (viewGroup.getChildCount() == 0) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < viewGroup.getChildCount(); i++) {
|
||||
traverseTree(viewGroup.getChildAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void onChildViewAdded(View parent, View child) {
|
||||
traverseTree(child);
|
||||
if (parent == RadioGroupPlus.this && child instanceof RadioButton) {
|
||||
int id = child.getId();
|
||||
// generates an id if it's missing
|
||||
if (id == View.NO_ID) {
|
||||
id = View.generateViewId();
|
||||
child.setId(id);
|
||||
}
|
||||
((RadioButton) child).setOnCheckedChangeListener(
|
||||
mChildOnCheckedChangeListener);
|
||||
}
|
||||
|
||||
if (mOnHierarchyChangeListener != null) {
|
||||
mOnHierarchyChangeListener.onChildViewAdded(parent, child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void onChildViewRemoved(View parent, View child) {
|
||||
if (parent == RadioGroupPlus.this && child instanceof RadioButton) {
|
||||
((RadioButton) child).setOnCheckedChangeListener(null);
|
||||
}
|
||||
|
||||
if (mOnHierarchyChangeListener != null) {
|
||||
mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue